White paper: Comparación de modelos predictivos de indicadores de rendimiento logístico

Autores: Hugo F. Tapia [1]

Filiación: Instituto de Ingeniería Industrial - Facultad de Ingeniería - UNCuyo

Contacto:

Correspondig author: [1]

DOI: …………..

Resumen

El trabajo analiza la construcción y evaluación de modelos predictivos para estimar el rendimiento logístico regional a partir de datos socioeconómicos. Basado en el trabajo previo (Tapia, Palma, and Forradellas 2023), que proponía un modelo de regresión lineal utilizando el Logistics Performance Index (LPI) y el PBI per cápita de la población económicamente activa (PBIPEA), este estudio amplía el análisis incorporando métodos de analítica de datos: árboles de decisión y redes neuronales. Para ello se conforma una base de datos con información internacional proveniente del World Bank, Enterprise Survey, Transparencia Internacional, FactbookCIA y el World Economic Forum. Se realiza un análisis exploratorio, correlacional y de componentes principales, seguido de técnicas de clustering (Clara, Agnes y Ward) para evaluar la reproducibilidad de los grupos definidos en (Tapia, Palma, and Forradellas 2023). Posteriormente se entrenan modelos predictivos, destacándose una alta precisión del modelo de árboles de decisión (89%) en la clasificación de grupos y un buen rendimiento del modelo de redes neuronales para la predicción del LPI (correlación del 82%). Finalmente, se estima el rendimiento logístico de las regiones de Mendoza mediante estos modelos y se comparan las predicciones con los valores obtenidos originalmente por regresiones lineales, verificándose una correlación aceptable entre ambos enfoques.

Palabras claves: LPI, predicción, modelos.

Introducción

En el estudio de la logística y las cadenas de abastecimiento a nivel regional existe una problemática que es la caracterización de esta por medio de indicadores. Si se buscan datos sobre rendimientos logísticos regionales no se encuentra información relevante sobre el tema (Tapia, Palma, and Forradellas 2023). A nivel mundial se obtiene una referencia por medio del Logistics Performance Index (LPI), que es un indicador logístico por países.

En (Tapia, Palma, and Forradellas 2023) se desarrolló un modelo predictivo para determinar el rendimiento logístico regional a un bajo costo, a través de la relación entre parámetros socioeconómicos regionales y el Logistics Performance Index (LPI). La metodología de la investigación fue la de generar en primer lugar una base de datos socio-económicos de organismos internacionales.

El análisis de los datos y el planteo del modelo se realizaron con ayuda de herramientas informáticas de análisis estadístico. Con los datos obtenidos en el estudio descriptivo se verificó la correlación de las variables planteadas. Se realizó un análisis exploratorio con planilla de cálculo y posteriormente se utilizó el software libre “R” para confirmar el análisis exploratorio y conformar un modelo de relación entre el LPI y los indicadores socioeconómicos. Se trató de establecer relaciones entre variables sin precisar sentido de causalidad. Las variables estudiadas son: LPI vs PBI per cápita de la población económicamente activa. Con lo cual se realizó un modelo predictivo por medio de regresiones lineales según actividades económicas. Para finalizar se realizó una aplicación a la provincia de Mendoza, donde se comparó el valor obtenido por el modelo predictivo y valores obtenidos de un estudio realizado en Mendoza (Tapia, Palma, and Moreno 2020).

Posteriormente se estimaron los valores del rendimiento para los distintos departamentos. Así, se estima que Mendoza tiene un rendimiento logístico promedio del 60%, pero cada micro región dentro de la provincia tiene sus particularidades que se observan en el trabajo.

Esta herramienta de bajo costo permite predecir el rendimiento logístico en distintas regiones, lo que ayuda a comprender el comportamiento logístico de las mismas. Como aplicación directa se podría determinar a través del rendimiento logístico y a nivel de prefactibilidad las necesidades de inversión en aspectos logísticos, como por ejemplo en infraestructuras.

En este trabajo se plantea como objetivo determinar la fiabilidad del modelo planteado en (Tapia, Palma, and Forradellas 2023) comparándolo con otros métodos de la analítica de datos como árboles de decisiones y redes neuronales. Finalizando con una aplicación a las regiones de la provincia de Mendoza.

Estado del Arte

La construcción de modelos predictivos requiere datos confiables. En logística, uno de los indicadores más robustos a nivel país es el LPI del World Bank (WB), complementado por datos socioeconómicos de otras fuentes internacionales. El LPI, vigente desde 2007, se compone de seis dimensiones vinculadas al desempeño logístico (Arvis et al. 2018) y se basa en encuestas a actores del transporte y la carga, lo cual lo hace útil pero también susceptible a errores de muestreo e interpretación. Aunque es la base de datos logística más completa, presenta limitaciones relacionadas con la experiencia de los encuestados y particularidades de países sin litoral marítimo o pequeños estados insulares (Arvis et al. 2018).

A nivel global persisten brechas significativas en los puntajes del LPI entre países según nivel de ingresos, aunque algunos de ingresos medios superan a sus pares, lo que evidencia que el ingreso no explica completamente el desempeño logístico (Arvis et al. 2018). Estudios sostienen que el rendimiento logístico está estrechamente vinculado con el desarrollo económico, pero también influido por factores políticos (Bı̂zoi and Sipos 2014). Asimismo, existe evidencia de la incidencia directa de la infraestructura, la política económica y la flexibilidad logística en el crecimiento nacional (Navickas, Sujeta, and Vojtovich 2011).

Investigaciones con amplias muestras de países muestran que la calidad de la infraestructura ferroviaria y portuaria es determinante del LPI (Khan et al., 2017), y que aduanas e infraestructura son los factores más relevantes en la relación entre LPI y PBI (Uca, Civelek, and Çemberci 2015). También se ha demostrado que el LPI media la relación entre el Índice de Percepción de la Corrupción y el comercio exterior, reforzando su rol en la competitividad (Civelek, Uca, and Çemberci 2015). Sorprendentemente, indicadores sociales muestran una relación más estrecha con el desempeño logístico que los económicos, cuestionando la idea de que mayores inversiones garantizan mejores resultados (Guner and Coskun 2012).

Otras investigaciones señalan que el LPI tiene un efecto moderador y mediador en la relación entre competitividad (GCI), PIB y desempeño logístico, confirmando que el LPI es un buen predictor del rendimiento económico (Çemberci, Civelek, and Canbolat 2015; D’aleo 2015). No obstante, algunos autores critican la metodología del WB, proponiendo ponderaciones diferenciadas de los componentes del LPI mediante el método BWM. Estas investigaciones otorgan mayor peso relativo a la infraestructura (0.24) y menor al seguimiento y rastreo (0.10), mostrando discrepancias con la ponderación uniforme actual (Rezaei, Roekel, and Tavasszy 2018). Finalmente, además del LPI, existen otros indicadores relevantes, como los provistos por el World Economic Forum, que incluyen métricas de infraestructura y desempeño de proveedores (World Economic Forum 2018).

Materiales y métodos

La metodología es no experimental transversal correlacional la cual consta de tres partes: conformación de base de datos, análisis y planteo de modelos. La conformación de la base de datos se realizó tomando la base de datos ocupada en (Tapia, Palma, and Forradellas 2023) y completándola con los datos obtenidos de esa investigación. Esta base de dato había sido desarrollada con datos de la web proveniente de la información publicada por diferentes organismos internacionales (World Bank, Enterprise Survey WB, FactbookCia, Transparency International y World Economic Forum) (Tapia 2021).

El modelo predictivo determinó 8 grupos (Grupo0, Grupo1, Grupo2, Grupo3, Grupo4, Grupo5, GrupoNC, GrupoRN). Cada uno caracterizados por relaciones de PBIPEA y actividades económicas. Los datos se observan en el archivo que conforma el dataset (adjunto:Datos_CursoAD_completo_1.xls ).

library(readxl)

Datos_CursoAD_completo_1 <- read_excel("Datos_CursoAD_completo_1.xls")

Para los grupos: 1; 2; 3; 4; 5 y RN se realizó un modelo de regresión lineal para predecir valores de rendimiento logístico. Los países que no pudieron clasificarse en los diferentes grupos, se los agrupó en el Grupo0 (PBIPEA<7000) y en el GrupoNC (NOC). En la siguiente figura se observan las diferentes regresiones.

library (knitr)
knitr::include_graphics ("Regresiones.png")
Regresiones lineales por grupos, extraído [@Tapia2021]

Regresiones lineales por grupos, extraído (Tapia 2021)

Para esta investigación se utilizará el software R, y los métodos KNN, árboles de decisiones y redes neuronales. Se compararán los valores de las predicciones para determinar la fiabilidad de los distintos métodos.

Además, se predecirán los valores de rendimiento logístico de las regiones de Mendoza, para lo cual se utilizará la siguiente base de datos. Para esta aplicación se recurre a los datos estadísticos del 2019 de la DEIE (Dirección de Estadísticas e Investigaciones económicas de Mendoza) para obtener los valores del Producto Bruto Geográfico (PBG), equivalente al PBI circunscripto al estudio de una región de un país. En esta base de datos se puede observar un resumen del PBG y su incidencia en las distintas actividades económicas, que es lo que demanda el modelo desarrollado (Tapia, Palma, and Forradellas 2023). Además, se considera la PEA, para calcular el coeficiente PBGPEA equivalente al PBIPEA. De esta manera se determinó cuáles son las regresiones que se deben utilizar para determinar el LPI. En el dataset correspondiente se observan los valores característicos de la región de Mendoza, al grupo que se determinó que pertenecían y las predicciones de los valores de LPI (adjunto: Datos_Mza_1.xls)

Datos_Mza_1 <- read_excel("Datos_Mza_1.xls")

Desarrollo

Análisis exploratorio

A continuación se procederá a la conformación de la base de datos para realizar el análisis exploratorio. Para que sea equievalente al planteado en (Tapia, Palma, and Forradellas 2023) se procederá a eliminar las filas que pertenezcan a los GrupoNC y Grupo0

library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
Datos_CursoAD_equivalente <- Datos_CursoAD_completo_1 %>% 
  filter(!Grupos %in% c("GrupoNC", "Grupo0"))
Datos_CursoAD_1 <- Datos_CursoAD_equivalente [ ,c(3,4,5,6,7,8,9,10,11,12,13,14,15)]

Relaciones entre variables

library (car)
## Loading required package: carData
## 
## Attaching package: 'car'
## The following object is masked from 'package:dplyr':
## 
##     recode
scatterplotMatrix(Datos_CursoAD_1)

Correlaciones entre variables

library(corrplot)
## corrplot 0.95 loaded
M <- cor (Datos_CursoAD_1)
corrplot(M)

library(psych)
## 
## Attaching package: 'psych'
## The following object is masked from 'package:car':
## 
##     logit
corPlot(Datos_CursoAD_1)

Se observa las mayores correlaciones para el LPI con las variables: PBIPEA, Agr, Exp_Merc, RRNN y Exp_H_Tech.

Matriz de covarianza

library(scatterPlotMatrix)
scatterPlotMatrix(Datos_CursoAD_1)

Determinación de componentes principales

mins <- sapply(Datos_CursoAD_1, min)
maxs <- sapply(Datos_CursoAD_1, max)
normalize <- function(x, min_val, max_val){
  (x - min_val) / (max_val - min_val)
}

Datos_CursoAD_1_norm <- as.data.frame(
  mapply(function(x, mn, mx) normalize(x, mn, mx),
         Datos_CursoAD_1, mins, maxs)
)
PC1 <- princomp (Datos_CursoAD_1_norm)
PC1
## Call:
## princomp(x = Datos_CursoAD_1_norm)
## 
## Standard deviations:
##     Comp.1     Comp.2     Comp.3     Comp.4     Comp.5     Comp.6     Comp.7 
## 0.41499408 0.31005196 0.23430173 0.21466658 0.17217622 0.14880397 0.11442578 
##     Comp.8     Comp.9    Comp.10    Comp.11    Comp.12    Comp.13 
## 0.10599135 0.08745451 0.07735274 0.05964044 0.04780932 0.02800634 
## 
##  13  variables and  118 observations.
plot(PC1)

biplot(PC1)

acp2 <-PC1$scores[ ,1:2]
plot(acp2)

Estudio de clústers

a- Agnes

library(cluster)
agp3 = agnes(acp2,method="average")
plot(agp3)

Se estudiarán la cantidad de 6 clústers que son los grupos determinados en el estudio de base.

Estudio por componentes principales

agpcut <- cutree(agp3,6)
par(mfrow=c(1,1))
plot(acp2,col=agpcut)

b- Clara

library(cluster)
plot(clara(Datos_CursoAD_1,6))

Se agregarán los números de clústers al dataset inicial

modelo <- clara(Datos_CursoAD_1, k = 6)
modelo$clustering
##   [1] 1 2 1 3 1 4 4 3 3 4 3 4 1 1 3 3 3 3 5 4 3 1 1 5 3 5 2 3 2 4 3 1 3 1 2 4 4
##  [38] 2 1 4 5 2 1 1 5 4 2 2 1 1 2 2 6 4 4 1 2 3 3 2 3 6 5 5 3 3 5 2 2 1 4 5 3 3
##  [75] 5 1 1 3 4 2 5 1 6 4 5 3 1 1 1 2 2 6 3 3 5 6 5 1 2 2 3 4 1 4 4 1 2 3 2 3 1
## [112] 4 4 4 3 5 3 5
clusters <- split(1:nrow(Datos_CursoAD_equivalente), modelo$clustering)
clusters
## $`1`
##  [1]   1   3   5  13  14  22  23  32  34  39  43  44  49  50  56  70  76  77  82
## [20]  87  88  89  98 103 106 111
## 
## $`2`
##  [1]   2  27  29  35  38  42  47  48  51  52  57  60  68  69  80  90  91  99 100
## [20] 107 109
## 
## $`3`
##  [1]   4   8   9  11  15  16  17  18  21  25  28  31  33  58  59  61  65  66  73
## [20]  74  78  86  93  94 101 108 110 115 117
## 
## $`4`
##  [1]   6   7  10  12  20  30  36  37  40  46  54  55  71  79  84 102 104 105 112
## [20] 113 114
## 
## $`5`
##  [1]  19  24  26  41  45  63  64  67  72  75  81  85  95  97 116 118
## 
## $`6`
## [1] 53 62 83 92 96
Datos_CursoAD_equivalente$cluster <- modelo$clustering

Se observa que la clasificación de clústers utilizada por el método Clara no es comparable con la clasificación en la fuente (Tapia, Palma, and Forradellas 2023)

c- Ward

Se utilizará el método Ward, que es el que se utilizó en (Tapia, Palma, and Forradellas 2023)

# 1. Escalar datos numéricos
Datos_CursoAD_1scaled <- scale(Datos_CursoAD_1)

# 2. Calcular distancia euclidiana
dist_euclid <- dist(Datos_CursoAD_1scaled, method = "euclidean")

# 3. Clustering jerárquico con método Ward
hc_ward <- hclust(dist_euclid, method = "ward.D2")

# 4. Graficar dendrograma
plot(hc_ward, 
     main = "Clustering Jerárquico - Método Ward (D2)",
     xlab = "",
     ylab = "Altura",
     sub = "")

# 5. Cortar el árbol en k clusters (ej: 3 clusters)
modelo2 <- cutree(hc_ward, k = 6)

# 6. Agregar la clasificación al dataset
Datos_CursoAD_cluster <- as.factor(modelo2)

# Ver clusters
table(Datos_CursoAD_cluster)
## Datos_CursoAD_cluster
##  1  2  3  4  5  6 
## 21 56 15 19  1  6
modelo2
##   [1] 1 2 2 2 1 3 4 2 2 4 2 4 1 2 2 2 2 2 2 3 3 2 2 1 2 1 2 2 4 4 2 2 2 2 4 3 3
##  [38] 2 2 4 1 2 2 1 1 5 4 3 1 2 2 6 4 3 3 2 3 2 2 4 2 6 1 1 2 2 2 4 4 2 4 1 2 2
##  [75] 2 1 2 2 4 2 1 1 6 6 1 2 1 3 2 2 2 6 2 2 1 6 1 2 4 4 2 3 1 3 4 2 2 2 3 2 2
## [112] 4 3 3 2 1 2 4

Se agregarán los números de clústers al dataset inicial

clusters2 <- split(1:nrow(Datos_CursoAD_equivalente), modelo2)
clusters2
## $`1`
##  [1]   1   5  13  24  26  41  44  45  49  63  64  72  76  81  82  85  87  95  97
## [20] 103 116
## 
## $`2`
##  [1]   2   3   4   8   9  11  14  15  16  17  18  19  22  23  25  27  28  31  32
## [20]  33  34  38  39  42  43  50  51  56  58  59  61  65  66  67  70  73  74  75
## [39]  77  78  80  86  89  90  91  93  94  98 101 106 107 108 110 111 115 117
## 
## $`3`
##  [1]   6  20  21  36  37  48  54  55  57  88 102 104 109 113 114
## 
## $`4`
##  [1]   7  10  12  29  30  35  40  47  53  60  68  69  71  79  99 100 105 112 118
## 
## $`5`
## [1] 46
## 
## $`6`
## [1] 52 62 83 84 92 96
Datos_CursoAD_equivalente$cluster2 <- modelo2
library(openxlsx)

write.xlsx(Datos_CursoAD_equivalente, "Datos_CursoAD_equivalente.xlsx")

De las dos maneras de obtener clústers no se observa demasiada conincidencia con lo planteado en (Tapia, Palma, and Forradellas 2023)

Modelos predictivos

Árboles de decisiones

library(rpart)
library(rpart.plot)   # para graficar el árbol
library (car)
library (party)
## Loading required package: grid
## Loading required package: mvtnorm
## Loading required package: modeltools
## Loading required package: stats4
## 
## Attaching package: 'modeltools'
## The following object is masked from 'package:car':
## 
##     Predict
## Loading required package: strucchange
## Loading required package: zoo
## 
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
## Loading required package: sandwich
## 
## Attaching package: 'party'
## The following object is masked from 'package:dplyr':
## 
##     where
Datos_CursoAD_Tree <- Datos_CursoAD_equivalente [ ,c(3,4,5,6,7,8,9,10,11,12,13,14,15,16)]

Datos_CursoAD_Tree$Grupos <- as.factor(Datos_CursoAD_Tree$Grupos)

Clasificación utilizando LPI y otro tipo de algoritmo para el árbol.

arbol <- rpart(Grupos ~ ., 
               data = Datos_CursoAD_Tree, 
               method = "class")   # clasificación
rpart.plot(arbol, type = 2, extra = 104, fallen.leaves = TRUE)

pred <- predict(arbol, Datos_CursoAD_Tree, type = "class")

table(Predicción = pred, Real = Datos_CursoAD_Tree$Grupos)
##           Real
## Predicción Grupo1 Grupo2 Grupo3 Grupo4 Grupo5 GrupoRN
##    Grupo1      17      2      0      0      0       2
##    Grupo2       2     40      0      0      0       0
##    Grupo3       0      0     16      0      0       0
##    Grupo4       0      0      2     11      0       0
##    Grupo5       0      0      0      1      6       0
##    GrupoRN      0      0      2      1      0      16

Se observa una matriz de confusión con una diagonal principal dominante. Con la siguiente efectividad de predicción.

mean(pred == Datos_CursoAD_Tree$Grupos)
## [1] 0.8983051
pred1 <- predict(arbol, Datos_Mza_1 [ , c (2,3,4,5,6,7,8,9,10,11,12,13,14)], type = "class")
pred1
##       1       2       3       4       5       6       7       8       9      10 
##  Grupo3  Grupo4  Grupo1  Grupo3  Grupo3  Grupo1  Grupo2  Grupo2  Grupo1  Grupo3 
##      11      12      13      14      15      16      17      18      19 
##  Grupo3 GrupoRN  Grupo1  Grupo2  Grupo2  Grupo2  Grupo1 GrupoRN  Grupo1 
## Levels: Grupo1 Grupo2 Grupo3 Grupo4 Grupo5 GrupoRN
Mza2 <- split(1:nrow(Datos_Mza_1), pred1)
Mza2
## $Grupo1
## [1]  3  6  9 13 17 19
## 
## $Grupo2
## [1]  7  8 14 15 16
## 
## $Grupo3
## [1]  1  4  5 10 11
## 
## $Grupo4
## [1] 2
## 
## $Grupo5
## integer(0)
## 
## $GrupoRN
## [1] 12 18
Datos_Mza_1$Mza2 <- pred1

write.xlsx(Datos_Mza_1, "Datos_Mza_1.xlsx")

Comparando los resultados del modelo predictivo de Grupos y la clasificación hecha en (Tapia, Palma, and Forradellas 2023) se observa una equivalencia del 89,47%.

Redes Neuronales

Se realizará una Red Neuronal para predecir los valores de LPI y compararlos con las predicciones de (Tapia, Palma, and Forradellas 2023) para Mendoza.

Primero se realiza un proceso de normalización.

mins <- sapply(Datos_CursoAD_1, min)
maxs <- sapply(Datos_CursoAD_1, max)
normalize <- function(x, min_val, max_val){
  (x - min_val) / (max_val - min_val)
}

Datos_CursoAD_1_norm <- as.data.frame(
  mapply(function(x, mn, mx) normalize(x, mn, mx),
         Datos_CursoAD_1, mins, maxs)
)

Segundo se divide el dataset en una muestra de entrenamiento y una muestra para testeo.

indice <- sample(2, nrow(Datos_CursoAD_1_norm), replace = TRUE, prob = c(0.7,0.3))
LPI_train <- Datos_CursoAD_1_norm [indice==1,]
LPI_test  <- Datos_CursoAD_1_norm [indice==2,]

Modelo predictivo de red neuronal.

library(neuralnet)
## 
## Attaching package: 'neuralnet'
## The following object is masked from 'package:dplyr':
## 
##     compute
LPI_model <- neuralnet(LPI ~ PBIPEA + Agr + Ind + Comercio + Exp_Mer + Exp_ByS + Exp_Comb + Exp_MM + Prod_oil + RRNN + Tierras_Agr + Exp_H_Tech , data = LPI_train, hidden = c(2,1))

str(LPI_model)
## List of 14
##  $ call               : language neuralnet(formula = LPI ~ PBIPEA + Agr + Ind + Comercio + Exp_Mer + Exp_ByS +      Exp_Comb + Exp_MM + Prod_oil +| __truncated__ ...
##  $ response           : num [1:91, 1] 0.226 0.426 0.216 0.825 0.107 ...
##   ..- attr(*, "dimnames")=List of 2
##   .. ..$ : chr [1:91] "1" "4" "5" "6" ...
##   .. ..$ : chr "LPI"
##  $ covariate          : num [1:91, 1:12] 0.1415 0.2508 0.0896 0.5464 0.1721 ...
##   ..- attr(*, "dimnames")=List of 2
##   .. ..$ : chr [1:91] "1" "4" "5" "6" ...
##   .. ..$ : chr [1:12] "PBIPEA" "Agr" "Ind" "Comercio" ...
##  $ model.list         :List of 2
##   ..$ response : chr "LPI"
##   ..$ variables: chr [1:12] "PBIPEA" "Agr" "Ind" "Comercio" ...
##  $ err.fct            :function (x, y)  
##   ..- attr(*, "type")= chr "sse"
##  $ act.fct            :function (x)  
##   ..- attr(*, "type")= chr "logistic"
##  $ linear.output      : logi TRUE
##  $ data               :'data.frame': 91 obs. of  13 variables:
##   ..$ PBIPEA     : num [1:91] 0.1415 0.2508 0.0896 0.5464 0.1721 ...
##   ..$ Agr        : num [1:91] 0.6378 0.2051 0.5256 0.0769 0.1795 ...
##   ..$ Ind        : num [1:91] 0.178 0.424 0.321 0.19 0.153 ...
##   ..$ Comercio   : num [1:91] 0.1683 0.0618 0.1448 0.0971 0.1725 ...
##   ..$ Exp_Mer    : num [1:91] 0.0417 0.0521 0.0489 0.1355 0.0642 ...
##   ..$ Exp_ByS    : num [1:91] 0.1545 0.0674 0.177 0.1032 0.2481 ...
##   ..$ Exp_Comb   : num [1:91] 0.0392 0.0131 0.0131 0.3072 0.4575 ...
##   ..$ Exp_MM     : num [1:91] 0.03378 0.01351 0.15541 0.36486 0.00676 ...
##   ..$ Prod_oil   : num [1:91] 0.013 0.0198 0 0.0173 0.1279 ...
##   ..$ RRNN       : num [1:91] 0.0447 0.0291 0.1074 0.123 0.4586 ...
##   ..$ Tierras_Agr: num [1:91] 0.511 0.651 0.708 0.569 0.693 ...
##   ..$ Exp_H_Tech : num [1:91] 0 0.0364 0.0242 0.1515 0.0121 ...
##   ..$ LPI        : num [1:91] 0.226 0.426 0.216 0.825 0.107 ...
##  $ exclude            : NULL
##  $ net.result         :List of 1
##   ..$ : num [1:91, 1] 0.1807 0.4884 0.2431 0.8368 0.0818 ...
##   .. ..- attr(*, "dimnames")=List of 2
##   .. .. ..$ : chr [1:91] "1" "4" "5" "6" ...
##   .. .. ..$ : NULL
##  $ weights            :List of 1
##   ..$ :List of 3
##   .. ..$ : num [1:13, 1:2] 0.992 -1.019 -0.417 -2.176 5.362 ...
##   .. ..$ : num [1:3, 1] 0.868 -1.025 1.947
##   .. ..$ : num [1:2, 1] -0.942 2.062
##  $ generalized.weights:List of 1
##   ..$ : num [1:91, 1:12] 1.36 2.37 1.54 1.7 2.61 ...
##   .. ..- attr(*, "dimnames")=List of 2
##   .. .. ..$ : chr [1:91] "1" "4" "5" "6" ...
##   .. .. ..$ : NULL
##  $ startweights       :List of 1
##   ..$ :List of 3
##   .. ..$ : num [1:13, 1:2] -0.0914 -0.7759 -0.3374 0.0652 1.4827 ...
##   .. ..$ : num [1:3, 1] 0.592 -0.329 0.719
##   .. ..$ : num [1:2, 1] -1.33 1.44
##  $ result.matrix      : num [1:34, 1] 0.44204 0.00994 220 0.99178 -1.01926 ...
##   ..- attr(*, "dimnames")=List of 2
##   .. ..$ : chr [1:34] "error" "reached.threshold" "steps" "Intercept.to.1layhid1" ...
##   .. ..$ : NULL
##  - attr(*, "class")= chr "nn"
library(NeuralNetTools)

plotnet(LPI_model, 
        alpha = 0.7,
        node_labs = TRUE,
        y_names = "LPI_pred",
        circle_cex = 5,
        cex_val = 0.6)

Se comprueba el nivel de eficiencia del modelo con respeto al set de testeo

model_results <- compute(LPI_model,LPI_test[1:13])
predicted_LPI <- model_results$net.result
cor(predicted_LPI,LPI_test$LPI)
##           [,1]
## [1,] 0.9175768

El nivel de correlación del testeo es del 86%

A continuación, se realiza la predicción de los valores para Mendoza.

Base de datos y normalización equivalente al set de datos originales

Mza_test<- Datos_Mza_1 [ ,c(2,3,4,5,6,7,8,9,10,11,12,13,14)]
Mza_test_norm <- as.data.frame(
  mapply(function(x, mn, mx) normalize(x, mn, mx),
         Mza_test, mins, maxs)
)

Predicciones de Mendoza

Mza_results <- compute(LPI_model,Mza_test_norm [1:12])
Mza_LPI_nomr <- Mza_results$net.result
Mza_LPI <- Mza_LPI_nomr * (maxs["LPI"] - mins["LPI"]) + mins["LPI"]
Mza_LPI
##           [,1]
##  [1,] 3.174461
##  [2,] 3.747156
##  [3,] 2.613310
##  [4,] 3.614318
##  [5,] 3.538232
##  [6,] 3.130821
##  [7,] 2.603535
##  [8,] 2.885250
##  [9,] 2.697581
## [10,] 3.953922
## [11,] 3.329446
## [12,] 2.556120
## [13,] 2.695386
## [14,] 2.862829
## [15,] 2.603730
## [16,] 2.825086
## [17,] 2.652929
## [18,] 2.236748
## [19,] 2.586546

Comparación de correlación entre los valores determinados por la red neuronal y el método de regresiones lineales observados en (Tapia, Palma, and Forradellas 2023)

cor(Mza_LPI,Datos_Mza_1$LPI)
##           [,1]
## [1,] 0.8875281
Mza3 <- split(1:nrow(Datos_Mza_1), Mza_LPI)
Datos_Mza_1$Mza3 <- Mza_LPI

write.xlsx(Datos_Mza_1, "Datos_Mza_1.xlsx")

Discución de Resultados

Clasificación por grupos

a. Clasificación por medio de Clústers

No se observan demasiadas similitudes entre los grupos determinados en (Tapia, Palma, and Forradellas 2023) y los obtenidos con los métodos de clústers utilizados en esta investigación.

b. Clasificación de grupos aplicados a Mendoza

Cuando se realiza este tipo de clasificación según la base de datos original, utilizando los Grupos preclasificados. Se utiliza un método predictivo por medio de árboles. Al comparar los resultados del método predictivo y lo determinado para Mendoza en el trabajo original, se obtiene una efectividad del 89% de los casos.

Esta es evidencia de que el método de clasificación por grupos utilizado en (Tapia, Palma, and Forradellas 2023) tiene una efectividad de casi el 90%.

Modelos predictivo de LPI

El desarrollo del modelo predictivo basado en Redes Neuronales presenta un buen coefiente de correlación del 82%. Comparable con el rango de valores de eficiencia de las regresiones lineales presentados en (Tapia, Palma, and Forradellas 2023), ver figura siguiente.

library (knitr)
knitr::include_graphics ("Test-regresiones.png")
Figura 1 – Eficiencia de Regresiones lineales por grupos, extraído [@Tapia2021]

Figura 1 – Eficiencia de Regresiones lineales por grupos, extraído (Tapia 2021)

Si se compara la predicción para Mendoza con redes neuronales y con el modelo de regresiones lineales, se observa una correlación aceptable de 0,72. La ventaja del modelo con redes neuronales tiene la ventaja de que no necesita una clasificación por grupos.

Conclusiones

El estudio presentado constituye un aporte significativo al análisis y predicción del rendimiento logístico regional, especialmente en contextos donde la información directa es escasa o inexistente. En primer lugar, queda demostrada la relevancia del LPI como indicador sintético y robusto a nivel internacional, así como la pertinencia de utilizar variables socioeconómicas para construir aproximaciones predictivas de bajo costo. La base de datos utilizada y ampliada en esta investigación permite establecer relaciones valiosas entre características económicas estructurales y desempeño logístico, generando un marco adecuado para la comparación de diversos métodos de analítica de datos.

En relación con los métodos predictivos, el análisis evidencia diferencias sustantivas entre las técnicas aplicadas. Los métodos de clustering —tanto por Clara como por Agnes y Ward— mostraron dificultades para reproducir los grupos previamente establecidos en (Tapia, Palma, and Forradellas 2023). Esto sugiere que la clasificación original depende de criterios específicos relacionados con las actividades económicas y la estructura productiva de los países, criterios que no emergen de manera espontánea mediante algoritmos de agrupamiento no supervisado. Sin embargo, al aplicar un proceso supervisado para la clasificación de grupos, el árbol de decisión obtuvo una precisión del 89%, lo que indica que los patrones presentes en la clasificación original pueden ser capturados adecuadamente cuando la técnica incorpora información previa.

El modelo de redes neuronales mostró también resultados prometedores. Con una correlación del 82% al evaluar el conjunto de testeo, se evidencia que este tipo de modelos tiene capacidad para aprender relaciones no lineales entre las variables socioeconómicas y el LPI, superando en flexibilidad a los modelos lineales tradicionales. Más aún, al comparar las predicciones para Mendoza con las estimaciones obtenidas mediante el método de regresiones lineales, se observó una correlación aceptable de 0,72. Este resultado confirma que las redes neuronales pueden ser una herramienta complementaria —e incluso alternativa— para la predicción del rendimiento logístico sin la necesidad de clasificar previamente a las regiones en grupos.

Desde una perspectiva aplicada, este trabajo demuestra la utilidad de los métodos de analítica de datos en el diseño de herramientas predictivas para la planificación logística regional. La posibilidad de estimar el rendimiento logístico con información disponible públicamente facilita la toma de decisiones en políticas públicas, especialmente en territorios donde no existen mediciones estandarizadas. Además, la comparación de métodos permite identificar fortalezas y limitaciones de cada enfoque: las regresiones lineales ofrecen interpretabilidad y simplicidad; los árboles de decisión aportan claridad en la clasificación; las redes neuronales permiten capturar relaciones complejas entre múltiples factores.

Finalmente, el análisis realizado para las regiones de Mendoza muestra el potencial del enfoque predictivo para aplicaciones territoriales concretas. La identificación de variaciones en el rendimiento logístico entre departamentos aporta información estratégica para orientar inversiones en infraestructura, optimizar la planificación y evaluar desigualdades territoriales. En conjunto, el estudio confirma que la combinación de datos socioeconómicos e inteligencia artificial constituye una herramienta sólida y eficaz para estimar indicadores logísticos y mejorar la comprensión del sistema productivo y territorial.

Referencias

# Referencias
Arvis, Jean-François, Lauri Ojala, Christian Wiederer, Ben Shepherd, Anusha Raj, Kamola Dairabayeva, and Tuomas Kiiski. 2018. Connecting to Compete 2018: Trade Logistics in the Global Economy. World Bank. https://doi.org/10.1596/29971.
Bı̂zoi, Anca-Cristina, and Cristina Sipos. 2014. “Logistics Performance and Economic Development: A Comparison Within the European Union.” https://doi.org/10.13140/2.1.1789.2163.
Çemberci, Muzaffer, Mehmet Emin Civelek, and Nesrin Canbolat. 2015. “The Moderator Effect of Global Competitiveness Index on Dimensions of Logistics Performance Index.” Procedia - Social and Behavioral Sciences 195: 1514–24. https://doi.org/10.1016/j.sbspro.2015.06.453.
Civelek, Mehmet Emin, Nurettin Uca, and Muzaffer Çemberci. 2015. “The Mediator Effect of Logistics Performance Index on the Relation Between Global Competitiveness Index and Gross Domestic Product.” European Scientific Journal, May.
D’aleo, Vittorio. 2015. “The Mediator Role of Logistic Performance Index: A Comparative Study.” Journal of International Trade, Logistics and Law 1 (1). http://www.jital.org/index.php/jital/article/view/26.
Guner, Selcuk, and Engin Coskun. 2012. “Comparison of Impacts of Economic and Social Factors on Countries’ Logistics Performances: A Study with 26 OECD Countries.” Research in Logistics and Production 2 (4): 329–43.
Navickas, Valentinas, Laura Sujeta, and Sergej Vojtovich. 2011. “Logistics Systems as a Factor of Country’s Competitiveness.” Economics and Management 16 (1): 231–37.
Rezaei, Jafar, Wessel S. van Roekel, and Lóri Tavasszy. 2018. “Measuring the Relative Importance of the Logistics Performance Index Indicators Using Best Worst Method.” Transport Policy 68: 158–69. https://doi.org/10.1016/j.tranpol.2018.05.007.
Tapia, Hugo Fernando. 2021. “Modelo Predictivo de Redes de Abastecimiento de Proyectos Con Uso Intensivo de Recursos.” Tesis de posgrado, Mendoza, Argentina: Universidad Nacional de Cuyo.
Tapia, Hugo Fernando, Raúl R. Palma, and Rodrigo Forradellas. 2023. “Modelo Predictivo de Rendimiento Logístico Aplicado a Mendoza.” In Décimo Primer Encuentro de Investigadores y Docentes de Ingeniería, 517–25.
Tapia, Hugo Fernando, Raúl R. Palma, and Jorge L. Moreno. 2020. “Indicadores de Cadenas de Abastecimiento En Proyectos de Construcción En Argentina.” Iberoamerican Journal of Project Management 11 (1): 94–115.
Uca, Nurettin, Mehmet Emin Civelek, and Muzaffer Çemberci. 2015. “The Effect of the Components of Logistics Performance Index on Gross Domestic Product: Conceptual Model Proposal.” Eurasian Business and Economics Journal 1 (1): 86–93. https://doi.org/10.17740/eas.econ.2015-v1-04.
World Economic Forum. 2018. “Data World Economic Forum.” https://www.weforum.org.
LS0tCnRpdGxlOiBDb21wYXJhY2nDs24gZGUgbW9kZWxvcyBwcmVkaWN0aXZvcyBkZSBpbmRpY2Fkb3JlcyBkZSByZW5kaW1pZW50bwogIGxvZ8Otc3RpY28KYXV0aG9yOiAiQ29ycmVzcG9uZGluZ19BdXRob3IgSHVnbyBGLiBUYXBpYSA8aGZ0cGlhQGdtYWlsLmNvbT4iCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKYmlibGlvZ3JhcGh5OiByZWZlcmVuY2lhcy5iaWIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogNQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCgojIyBXaGl0ZSBwYXBlcjogQ29tcGFyYWNpw7NuIGRlIG1vZGVsb3MgcHJlZGljdGl2b3MgZGUgaW5kaWNhZG9yZXMgZGUgcmVuZGltaWVudG8gbG9nw61zdGljbwojIyMgQXV0b3JlczogICBIdWdvIEYuIFRhcGlhIFsxXQojIyMgRmlsaWFjacOzbjogSW5zdGl0dXRvIGRlIEluZ2VuaWVyw61hIEluZHVzdHJpYWwgLSBGYWN1bHRhZCBkZSBJbmdlbmllcsOtYSAtIFVOQ3V5bwojIyMgQ29udGFjdG86IGhmdGFwaWFAZ21haWwuY29tIAojIyMgQ29ycmVzcG9uZGlnIGF1dGhvcjogWzFdCiMjIyBET0k6IC4uLi4uLi4uLi4uLi4uCgojIyBSZXN1bWVuCgpFbCB0cmFiYWpvIGFuYWxpemEgbGEgY29uc3RydWNjacOzbiB5IGV2YWx1YWNpw7NuIGRlIG1vZGVsb3MgcHJlZGljdGl2b3MgcGFyYSBlc3RpbWFyIGVsIHJlbmRpbWllbnRvIGxvZ8Otc3RpY28gcmVnaW9uYWwgYSBwYXJ0aXIgZGUgZGF0b3Mgc29jaW9lY29uw7NtaWNvcy4gQmFzYWRvIGVuIGVsIHRyYWJham8gcHJldmlvIFtAVGFwaWEyMDIzXSwgcXVlIHByb3BvbsOtYSB1biBtb2RlbG8gZGUgcmVncmVzacOzbiBsaW5lYWwgdXRpbGl6YW5kbyBlbCBMb2dpc3RpY3MgUGVyZm9ybWFuY2UgSW5kZXggKExQSSkgeSBlbCBQQkkgcGVyIGPDoXBpdGEgZGUgbGEgcG9ibGFjacOzbiBlY29uw7NtaWNhbWVudGUgYWN0aXZhIChQQklQRUEpLCBlc3RlIGVzdHVkaW8gYW1wbMOtYSBlbCBhbsOhbGlzaXMgaW5jb3Jwb3JhbmRvIG3DqXRvZG9zIGRlIGFuYWzDrXRpY2EgZGUgZGF0b3M6IMOhcmJvbGVzIGRlIGRlY2lzacOzbiB5IHJlZGVzIG5ldXJvbmFsZXMuIFBhcmEgZWxsbyBzZSBjb25mb3JtYSB1bmEgYmFzZSBkZSBkYXRvcyBjb24gaW5mb3JtYWNpw7NuIGludGVybmFjaW9uYWwgcHJvdmVuaWVudGUgZGVsIFdvcmxkIEJhbmssIEVudGVycHJpc2UgU3VydmV5LCBUcmFuc3BhcmVuY2lhIEludGVybmFjaW9uYWwsIEZhY3Rib29rQ0lBIHkgZWwgV29ybGQgRWNvbm9taWMgRm9ydW0uIFNlIHJlYWxpemEgdW4gYW7DoWxpc2lzIGV4cGxvcmF0b3JpbywgY29ycmVsYWNpb25hbCB5IGRlIGNvbXBvbmVudGVzIHByaW5jaXBhbGVzLCBzZWd1aWRvIGRlIHTDqWNuaWNhcyBkZSBjbHVzdGVyaW5nIChDbGFyYSwgQWduZXMgeSBXYXJkKSBwYXJhIGV2YWx1YXIgbGEgcmVwcm9kdWNpYmlsaWRhZCBkZSBsb3MgZ3J1cG9zIGRlZmluaWRvcyBlbiBbQFRhcGlhMjAyM10uIFBvc3Rlcmlvcm1lbnRlIHNlIGVudHJlbmFuIG1vZGVsb3MgcHJlZGljdGl2b3MsIGRlc3RhY8OhbmRvc2UgdW5hIGFsdGEgcHJlY2lzacOzbiBkZWwgbW9kZWxvIGRlIMOhcmJvbGVzIGRlIGRlY2lzacOzbiAoODklKSBlbiBsYSBjbGFzaWZpY2FjacOzbiBkZSBncnVwb3MgeSB1biBidWVuIHJlbmRpbWllbnRvIGRlbCBtb2RlbG8gZGUgcmVkZXMgbmV1cm9uYWxlcyBwYXJhIGxhIHByZWRpY2Npw7NuIGRlbCBMUEkgKGNvcnJlbGFjacOzbiBkZWwgODIlKS4gRmluYWxtZW50ZSwgc2UgZXN0aW1hIGVsIHJlbmRpbWllbnRvIGxvZ8Otc3RpY28gZGUgbGFzIHJlZ2lvbmVzIGRlIE1lbmRvemEgbWVkaWFudGUgZXN0b3MgbW9kZWxvcyB5IHNlIGNvbXBhcmFuIGxhcyBwcmVkaWNjaW9uZXMgY29uIGxvcyB2YWxvcmVzIG9idGVuaWRvcyBvcmlnaW5hbG1lbnRlIHBvciByZWdyZXNpb25lcyBsaW5lYWxlcywgdmVyaWZpY8OhbmRvc2UgdW5hIGNvcnJlbGFjacOzbiBhY2VwdGFibGUgZW50cmUgYW1ib3MgZW5mb3F1ZXMuCgpQYWxhYnJhcyBjbGF2ZXM6IExQSSwgcHJlZGljY2nDs24sIG1vZGVsb3MuCgojIyBJbnRyb2R1Y2Npw7NuCgpFbiBlbCBlc3R1ZGlvIGRlIGxhIGxvZ8Otc3RpY2EgeSBsYXMgY2FkZW5hcyBkZSBhYmFzdGVjaW1pZW50byBhIG5pdmVsIHJlZ2lvbmFsIGV4aXN0ZSB1bmEgcHJvYmxlbcOhdGljYSBxdWUgZXMgbGEgY2FyYWN0ZXJpemFjacOzbiBkZSBlc3RhIHBvciBtZWRpbyBkZSBpbmRpY2Fkb3Jlcy4gU2kgc2UgYnVzY2FuIGRhdG9zIHNvYnJlIHJlbmRpbWllbnRvcyBsb2fDrXN0aWNvcyByZWdpb25hbGVzIG5vIHNlIGVuY3VlbnRyYSBpbmZvcm1hY2nDs24gcmVsZXZhbnRlIHNvYnJlIGVsIHRlbWEgW0BUYXBpYTIwMjNdLiAgQSBuaXZlbCBtdW5kaWFsIHNlIG9idGllbmUgdW5hIHJlZmVyZW5jaWEgcG9yIG1lZGlvIGRlbCBMb2dpc3RpY3MgUGVyZm9ybWFuY2UgSW5kZXggKExQSSksIHF1ZSBlcyB1biBpbmRpY2Fkb3IgbG9nw61zdGljbyBwb3IgcGHDrXNlcy4gCgpFbiBbQFRhcGlhMjAyM10gc2UgZGVzYXJyb2xsw7MgdW4gbW9kZWxvIHByZWRpY3Rpdm8gcGFyYSBkZXRlcm1pbmFyIGVsIHJlbmRpbWllbnRvIGxvZ8Otc3RpY28gcmVnaW9uYWwgYSB1biBiYWpvIGNvc3RvLCBhIHRyYXbDqXMgZGUgbGEgcmVsYWNpw7NuIGVudHJlIHBhcsOhbWV0cm9zIHNvY2lvZWNvbsOzbWljb3MgcmVnaW9uYWxlcyB5IGVsIExvZ2lzdGljcyBQZXJmb3JtYW5jZSBJbmRleCAoTFBJKS4gTGEgbWV0b2RvbG9nw61hIGRlIGxhIGludmVzdGlnYWNpw7NuIGZ1ZSBsYSBkZSBnZW5lcmFyIGVuIHByaW1lciBsdWdhciB1bmEgYmFzZSBkZSBkYXRvcyBzb2Npby1lY29uw7NtaWNvcyBkZSBvcmdhbmlzbW9zIGludGVybmFjaW9uYWxlcy4gCgpFbCBhbsOhbGlzaXMgZGUgbG9zIGRhdG9zIHkgZWwgcGxhbnRlbyBkZWwgbW9kZWxvIHNlIHJlYWxpemFyb24gY29uIGF5dWRhIGRlIGhlcnJhbWllbnRhcyBpbmZvcm3DoXRpY2FzIGRlIGFuw6FsaXNpcyBlc3RhZMOtc3RpY28uIENvbiBsb3MgZGF0b3Mgb2J0ZW5pZG9zIGVuIGVsIGVzdHVkaW8gZGVzY3JpcHRpdm8gc2UgdmVyaWZpY8OzIGxhIGNvcnJlbGFjacOzbiBkZSBsYXMgdmFyaWFibGVzIHBsYW50ZWFkYXMuIFNlIHJlYWxpesOzIHVuIGFuw6FsaXNpcyBleHBsb3JhdG9yaW8gY29uIHBsYW5pbGxhIGRlIGPDoWxjdWxvIHkgcG9zdGVyaW9ybWVudGUgc2UgdXRpbGl6w7MgZWwgc29mdHdhcmUgbGlicmUg4oCcUuKAnSBwYXJhIGNvbmZpcm1hciBlbCBhbsOhbGlzaXMgZXhwbG9yYXRvcmlvIHkgY29uZm9ybWFyIHVuIG1vZGVsbyBkZSByZWxhY2nDs24gZW50cmUgZWwgTFBJIHkgbG9zIGluZGljYWRvcmVzIHNvY2lvZWNvbsOzbWljb3MuIFNlIHRyYXTDsyBkZSBlc3RhYmxlY2VyIHJlbGFjaW9uZXMgZW50cmUgdmFyaWFibGVzIHNpbiBwcmVjaXNhciBzZW50aWRvIGRlIGNhdXNhbGlkYWQuIExhcyB2YXJpYWJsZXMgZXN0dWRpYWRhcyBzb246IExQSSB2cyBQQkkgcGVyIGPDoXBpdGEgZGUgbGEgcG9ibGFjacOzbiBlY29uw7NtaWNhbWVudGUgYWN0aXZhLiBDb24gbG8gY3VhbCBzZSByZWFsaXrDsyB1biBtb2RlbG8gcHJlZGljdGl2byBwb3IgbWVkaW8gZGUgcmVncmVzaW9uZXMgbGluZWFsZXMgc2Vnw7puIGFjdGl2aWRhZGVzIGVjb27Ds21pY2FzLiBQYXJhIGZpbmFsaXphciBzZSByZWFsaXrDsyB1bmEgYXBsaWNhY2nDs24gYSBsYSBwcm92aW5jaWEgZGUgTWVuZG96YSwgZG9uZGUgc2UgY29tcGFyw7MgZWwgdmFsb3Igb2J0ZW5pZG8gcG9yIGVsIG1vZGVsbyBwcmVkaWN0aXZvIHkgdmFsb3JlcyBvYnRlbmlkb3MgZGUgdW4gZXN0dWRpbyByZWFsaXphZG8gZW4gTWVuZG96YSBbQFRhcGlhMjAyMF0uCgpQb3N0ZXJpb3JtZW50ZSBzZSBlc3RpbWFyb24gbG9zIHZhbG9yZXMgZGVsIHJlbmRpbWllbnRvIHBhcmEgbG9zIGRpc3RpbnRvcyBkZXBhcnRhbWVudG9zLiBBc8OtLCBzZSBlc3RpbWEgcXVlIE1lbmRvemEgdGllbmUgdW4gcmVuZGltaWVudG8gbG9nw61zdGljbyBwcm9tZWRpbyBkZWwgNjAlLCBwZXJvIGNhZGEgbWljcm8gcmVnacOzbiBkZW50cm8gZGUgbGEgcHJvdmluY2lhIHRpZW5lIHN1cyBwYXJ0aWN1bGFyaWRhZGVzIHF1ZSBzZSBvYnNlcnZhbiBlbiBlbCB0cmFiYWpvLiAKCkVzdGEgaGVycmFtaWVudGEgZGUgYmFqbyBjb3N0byBwZXJtaXRlIHByZWRlY2lyIGVsIHJlbmRpbWllbnRvIGxvZ8Otc3RpY28gZW4gZGlzdGludGFzIHJlZ2lvbmVzLCBsbyBxdWUgYXl1ZGEgYSBjb21wcmVuZGVyIGVsIGNvbXBvcnRhbWllbnRvIGxvZ8Otc3RpY28gZGUgbGFzIG1pc21hcy4gQ29tbyBhcGxpY2FjacOzbiBkaXJlY3RhIHNlIHBvZHLDrWEgZGV0ZXJtaW5hciBhIHRyYXbDqXMgZGVsIHJlbmRpbWllbnRvIGxvZ8Otc3RpY28geSBhIG5pdmVsIGRlIHByZWZhY3RpYmlsaWRhZCBsYXMgbmVjZXNpZGFkZXMgZGUgaW52ZXJzacOzbiBlbiBhc3BlY3RvcyBsb2fDrXN0aWNvcywgY29tbyBwb3IgZWplbXBsbyBlbiBpbmZyYWVzdHJ1Y3R1cmFzLiAKCkVuIGVzdGUgdHJhYmFqbyBzZSBwbGFudGVhIGNvbW8gb2JqZXRpdm8gZGV0ZXJtaW5hciBsYSBmaWFiaWxpZGFkIGRlbCBtb2RlbG8gcGxhbnRlYWRvIGVuIFtAVGFwaWEyMDIzXSBjb21wYXLDoW5kb2xvIGNvbiBvdHJvcyBtw6l0b2RvcyBkZSBsYSBhbmFsw610aWNhIGRlIGRhdG9zIGNvbW8gw6FyYm9sZXMgZGUgZGVjaXNpb25lcyB5IHJlZGVzIG5ldXJvbmFsZXMuIEZpbmFsaXphbmRvIGNvbiB1bmEgYXBsaWNhY2nDs24gYSBsYXMgcmVnaW9uZXMgZGUgbGEgcHJvdmluY2lhIGRlIE1lbmRvemEuIAoKCiMjIEVzdGFkbyBkZWwgQXJ0ZQoKTGEgY29uc3RydWNjacOzbiBkZSBtb2RlbG9zIHByZWRpY3Rpdm9zIHJlcXVpZXJlIGRhdG9zIGNvbmZpYWJsZXMuIEVuIGxvZ8Otc3RpY2EsIHVubyBkZSBsb3MgaW5kaWNhZG9yZXMgbcOhcyByb2J1c3RvcyBhIG5pdmVsIHBhw61zIGVzIGVsIExQSSBkZWwgV29ybGQgQmFuayAoV0IpLCBjb21wbGVtZW50YWRvIHBvciBkYXRvcyBzb2Npb2Vjb27Ds21pY29zIGRlIG90cmFzIGZ1ZW50ZXMgaW50ZXJuYWNpb25hbGVzLiBFbCBMUEksIHZpZ2VudGUgZGVzZGUgMjAwNywgc2UgY29tcG9uZSBkZSBzZWlzIGRpbWVuc2lvbmVzIHZpbmN1bGFkYXMgYWwgZGVzZW1wZcOxbyBsb2fDrXN0aWNvIFtAQXJ2aXMyMDE4XSB5IHNlIGJhc2EgZW4gZW5jdWVzdGFzIGEgYWN0b3JlcyBkZWwgdHJhbnNwb3J0ZSB5IGxhIGNhcmdhLCBsbyBjdWFsIGxvIGhhY2Ugw7p0aWwgcGVybyB0YW1iacOpbiBzdXNjZXB0aWJsZSBhIGVycm9yZXMgZGUgbXVlc3RyZW8gZSBpbnRlcnByZXRhY2nDs24uIEF1bnF1ZSBlcyBsYSBiYXNlIGRlIGRhdG9zIGxvZ8Otc3RpY2EgbcOhcyBjb21wbGV0YSwgcHJlc2VudGEgbGltaXRhY2lvbmVzIHJlbGFjaW9uYWRhcyBjb24gbGEgZXhwZXJpZW5jaWEgZGUgbG9zIGVuY3Vlc3RhZG9zIHkgcGFydGljdWxhcmlkYWRlcyBkZSBwYcOtc2VzIHNpbiBsaXRvcmFsIG1hcsOtdGltbyBvIHBlcXVlw7FvcyBlc3RhZG9zIGluc3VsYXJlcyBbQEFydmlzMjAxOF0uCgpBIG5pdmVsIGdsb2JhbCBwZXJzaXN0ZW4gYnJlY2hhcyBzaWduaWZpY2F0aXZhcyBlbiBsb3MgcHVudGFqZXMgZGVsIExQSSBlbnRyZSBwYcOtc2VzIHNlZ8O6biBuaXZlbCBkZSBpbmdyZXNvcywgYXVucXVlIGFsZ3Vub3MgZGUgaW5ncmVzb3MgbWVkaW9zIHN1cGVyYW4gYSBzdXMgcGFyZXMsIGxvIHF1ZSBldmlkZW5jaWEgcXVlIGVsIGluZ3Jlc28gbm8gZXhwbGljYSBjb21wbGV0YW1lbnRlIGVsIGRlc2VtcGXDsW8gbG9nw61zdGljbyBbQEFydmlzMjAxOF0uIEVzdHVkaW9zIHNvc3RpZW5lbiBxdWUgZWwgcmVuZGltaWVudG8gbG9nw61zdGljbyBlc3TDoSBlc3RyZWNoYW1lbnRlIHZpbmN1bGFkbyBjb24gZWwgZGVzYXJyb2xsbyBlY29uw7NtaWNvLCBwZXJvIHRhbWJpw6luIGluZmx1aWRvIHBvciBmYWN0b3JlcyBwb2zDrXRpY29zIFtAQml6b2kyMDE0XS4gQXNpbWlzbW8sIGV4aXN0ZSBldmlkZW5jaWEgZGUgbGEgaW5jaWRlbmNpYSBkaXJlY3RhIGRlIGxhIGluZnJhZXN0cnVjdHVyYSwgbGEgcG9sw610aWNhIGVjb27Ds21pY2EgeSBsYSBmbGV4aWJpbGlkYWQgbG9nw61zdGljYSBlbiBlbCBjcmVjaW1pZW50byBuYWNpb25hbCBbQE5hdmlja2FzMjAxMV0uCgpJbnZlc3RpZ2FjaW9uZXMgY29uIGFtcGxpYXMgbXVlc3RyYXMgZGUgcGHDrXNlcyBtdWVzdHJhbiBxdWUgbGEgY2FsaWRhZCBkZSBsYSBpbmZyYWVzdHJ1Y3R1cmEgZmVycm92aWFyaWEgeSBwb3J0dWFyaWEgZXMgZGV0ZXJtaW5hbnRlIGRlbCBMUEkgKEtoYW4gZXQgYWwuLCAyMDE3KSwgeSBxdWUgYWR1YW5hcyBlIGluZnJhZXN0cnVjdHVyYSBzb24gbG9zIGZhY3RvcmVzIG3DoXMgcmVsZXZhbnRlcyBlbiBsYSByZWxhY2nDs24gZW50cmUgTFBJIHkgUEJJIFtAVWNhMjAxNV0uIFRhbWJpw6luIHNlIGhhIGRlbW9zdHJhZG8gcXVlIGVsIExQSSBtZWRpYSBsYSByZWxhY2nDs24gZW50cmUgZWwgw41uZGljZSBkZSBQZXJjZXBjacOzbiBkZSBsYSBDb3JydXBjacOzbiB5IGVsIGNvbWVyY2lvIGV4dGVyaW9yLCByZWZvcnphbmRvIHN1IHJvbCBlbiBsYSBjb21wZXRpdGl2aWRhZCBbQENpdmVsZWsyMDE1XS4gU29ycHJlbmRlbnRlbWVudGUsIGluZGljYWRvcmVzIHNvY2lhbGVzIG11ZXN0cmFuIHVuYSByZWxhY2nDs24gbcOhcyBlc3RyZWNoYSBjb24gZWwgZGVzZW1wZcOxbyBsb2fDrXN0aWNvIHF1ZSBsb3MgZWNvbsOzbWljb3MsIGN1ZXN0aW9uYW5kbyBsYSBpZGVhIGRlIHF1ZSBtYXlvcmVzIGludmVyc2lvbmVzIGdhcmFudGl6YW4gbWVqb3JlcyByZXN1bHRhZG9zIFtAR3VuZXIyMDEyXS4KCk90cmFzIGludmVzdGlnYWNpb25lcyBzZcOxYWxhbiBxdWUgZWwgTFBJIHRpZW5lIHVuIGVmZWN0byBtb2RlcmFkb3IgeSBtZWRpYWRvciBlbiBsYSByZWxhY2nDs24gZW50cmUgY29tcGV0aXRpdmlkYWQgKEdDSSksIFBJQiB5IGRlc2VtcGXDsW8gbG9nw61zdGljbywgY29uZmlybWFuZG8gcXVlIGVsIExQSSBlcyB1biBidWVuIHByZWRpY3RvciBkZWwgcmVuZGltaWVudG8gZWNvbsOzbWljbyBbQENlbWJlcmNpMjAxNSA7IEBEYWxlbzIwMTVdLiBObyBvYnN0YW50ZSwgYWxndW5vcyBhdXRvcmVzIGNyaXRpY2FuIGxhIG1ldG9kb2xvZ8OtYSBkZWwgV0IsIHByb3BvbmllbmRvIHBvbmRlcmFjaW9uZXMgZGlmZXJlbmNpYWRhcyBkZSBsb3MgY29tcG9uZW50ZXMgZGVsIExQSSBtZWRpYW50ZSBlbCBtw6l0b2RvIEJXTS4gRXN0YXMgaW52ZXN0aWdhY2lvbmVzIG90b3JnYW4gbWF5b3IgcGVzbyByZWxhdGl2byBhIGxhIGluZnJhZXN0cnVjdHVyYSAoMC4yNCkgeSBtZW5vciBhbCBzZWd1aW1pZW50byB5IHJhc3RyZW8gKDAuMTApLCBtb3N0cmFuZG8gZGlzY3JlcGFuY2lhcyBjb24gbGEgcG9uZGVyYWNpw7NuIHVuaWZvcm1lIGFjdHVhbCBbQFJlemFlaTIwMThdLgpGaW5hbG1lbnRlLCBhZGVtw6FzIGRlbCBMUEksIGV4aXN0ZW4gb3Ryb3MgaW5kaWNhZG9yZXMgcmVsZXZhbnRlcywgY29tbyBsb3MgcHJvdmlzdG9zIHBvciBlbCBXb3JsZCBFY29ub21pYyBGb3J1bSwgcXVlIGluY2x1eWVuIG3DqXRyaWNhcyBkZSBpbmZyYWVzdHJ1Y3R1cmEgeSBkZXNlbXBlw7FvIGRlIHByb3ZlZWRvcmVzIFtAV0VGb3J1bTIwMThdLgoKCiMjIE1hdGVyaWFsZXMgeSBtw6l0b2RvcwoKTGEgbWV0b2RvbG9nw61hIGVzIG5vIGV4cGVyaW1lbnRhbCB0cmFuc3ZlcnNhbCBjb3JyZWxhY2lvbmFsIGxhIGN1YWwgY29uc3RhIGRlIHRyZXMgcGFydGVzOiBjb25mb3JtYWNpw7NuIGRlIGJhc2UgZGUgZGF0b3MsIGFuw6FsaXNpcyB5IHBsYW50ZW8gZGUgbW9kZWxvcy4gTGEgY29uZm9ybWFjacOzbiBkZSBsYSBiYXNlIGRlIGRhdG9zIHNlIHJlYWxpesOzIHRvbWFuZG8gbGEgYmFzZSBkZSBkYXRvcyBvY3VwYWRhIGVuIFtAVGFwaWEyMDIzXSB5IGNvbXBsZXTDoW5kb2xhIGNvbiBsb3MgZGF0b3Mgb2J0ZW5pZG9zIGRlIGVzYSBpbnZlc3RpZ2FjacOzbi4gRXN0YSBiYXNlIGRlIGRhdG8gaGFiw61hIHNpZG8gZGVzYXJyb2xsYWRhIGNvbiBkYXRvcyBkZSBsYSB3ZWIgcHJvdmVuaWVudGUgZGUgbGEgaW5mb3JtYWNpw7NuIHB1YmxpY2FkYSBwb3IgZGlmZXJlbnRlcyBvcmdhbmlzbW9zIGludGVybmFjaW9uYWxlcyAoV29ybGQgQmFuaywgRW50ZXJwcmlzZSBTdXJ2ZXkgV0IsIEZhY3Rib29rQ2lhLCBUcmFuc3BhcmVuY3kgSW50ZXJuYXRpb25hbCB5IFdvcmxkIEVjb25vbWljIEZvcnVtKSBbQFRhcGlhMjAyMV0uIAoKRWwgbW9kZWxvIHByZWRpY3Rpdm8gZGV0ZXJtaW7DsyA4IGdydXBvcyAoR3J1cG8wLCBHcnVwbzEsIEdydXBvMiwgR3J1cG8zLCBHcnVwbzQsIEdydXBvNSwgR3J1cG9OQywgR3J1cG9STikuIENhZGEgdW5vIGNhcmFjdGVyaXphZG9zIHBvciByZWxhY2lvbmVzIGRlIFBCSVBFQSB5IGFjdGl2aWRhZGVzIGVjb27Ds21pY2FzLiBMb3MgZGF0b3Mgc2Ugb2JzZXJ2YW4gZW4gZWwgYXJjaGl2byBxdWUgY29uZm9ybWEgZWwgZGF0YXNldCAoYWRqdW50bzpEYXRvc19DdXJzb0FEX2NvbXBsZXRvXzEueGxzICkuCgpgYGB7cn0KbGlicmFyeShyZWFkeGwpCgpEYXRvc19DdXJzb0FEX2NvbXBsZXRvXzEgPC0gcmVhZF9leGNlbCgiRGF0b3NfQ3Vyc29BRF9jb21wbGV0b18xLnhscyIpCgpgYGAKCgpQYXJhIGxvcyBncnVwb3M6IDE7IDI7IDM7IDQ7IDUgeSBSTiBzZSByZWFsaXrDsyB1biBtb2RlbG8gZGUgcmVncmVzacOzbiBsaW5lYWwgcGFyYSBwcmVkZWNpciB2YWxvcmVzIGRlIHJlbmRpbWllbnRvIGxvZ8Otc3RpY28uIExvcyBwYcOtc2VzIHF1ZSBubyBwdWRpZXJvbiBjbGFzaWZpY2Fyc2UgZW4gbG9zIGRpZmVyZW50ZXMgZ3J1cG9zLCBzZSBsb3MgYWdydXDDsyBlbiBlbCBHcnVwbzAgKFBCSVBFQTw3MDAwKSB5IGVuIGVsIEdydXBvTkMgKE5PQykuIEVuIGxhIHNpZ3VpZW50ZSBmaWd1cmEgc2Ugb2JzZXJ2YW4gbGFzIGRpZmVyZW50ZXMgcmVncmVzaW9uZXMuIAoKCmBgYHtyLCBmaWcuY2FwPSJSZWdyZXNpb25lcyBsaW5lYWxlcyBwb3IgZ3J1cG9zLCBleHRyYcOtZG8gW0BUYXBpYTIwMjFdIiwgb3V0LndpZHRoPSI4MCUiLCBmaWcuYWxpZ249ImNlbnRlciJ9CmxpYnJhcnkgKGtuaXRyKQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyAoIlJlZ3Jlc2lvbmVzLnBuZyIpCmBgYAoKUGFyYSBlc3RhIGludmVzdGlnYWNpw7NuIHNlIHV0aWxpemFyw6EgZWwgc29mdHdhcmUgUiwgeSBsb3MgbcOpdG9kb3MgS05OLCDDoXJib2xlcyBkZSBkZWNpc2lvbmVzIHkgcmVkZXMgbmV1cm9uYWxlcy4gU2UgY29tcGFyYXLDoW4gbG9zIHZhbG9yZXMgZGUgbGFzIHByZWRpY2Npb25lcyBwYXJhIGRldGVybWluYXIgbGEgZmlhYmlsaWRhZCBkZSBsb3MgZGlzdGludG9zIG3DqXRvZG9zLiAKCkFkZW3DoXMsIHNlIHByZWRlY2lyw6FuIGxvcyB2YWxvcmVzIGRlIHJlbmRpbWllbnRvIGxvZ8Otc3RpY28gZGUgbGFzIHJlZ2lvbmVzIGRlIE1lbmRvemEsIHBhcmEgbG8gY3VhbCBzZSB1dGlsaXphcsOhIGxhIHNpZ3VpZW50ZSBiYXNlIGRlIGRhdG9zLiBQYXJhIGVzdGEgYXBsaWNhY2nDs24gc2UgcmVjdXJyZSBhIGxvcyBkYXRvcyBlc3RhZMOtc3RpY29zIGRlbCAyMDE5IGRlIGxhIERFSUUgKERpcmVjY2nDs24gZGUgRXN0YWTDrXN0aWNhcyBlIEludmVzdGlnYWNpb25lcyBlY29uw7NtaWNhcyBkZSBNZW5kb3phKSBwYXJhIG9idGVuZXIgbG9zIHZhbG9yZXMgZGVsIFByb2R1Y3RvIEJydXRvIEdlb2dyw6FmaWNvIChQQkcpLCBlcXVpdmFsZW50ZSBhbCBQQkkgY2lyY3Vuc2NyaXB0byBhbCBlc3R1ZGlvIGRlIHVuYSByZWdpw7NuIGRlIHVuIHBhw61zLiBFbiBlc3RhIGJhc2UgZGUgZGF0b3Mgc2UgcHVlZGUgb2JzZXJ2YXIgdW4gcmVzdW1lbiBkZWwgUEJHIHkgc3UgaW5jaWRlbmNpYSBlbiBsYXMgZGlzdGludGFzIGFjdGl2aWRhZGVzIGVjb27Ds21pY2FzLCBxdWUgZXMgbG8gcXVlIGRlbWFuZGEgZWwgbW9kZWxvIGRlc2Fycm9sbGFkbyBbQFRhcGlhMjAyM10uIEFkZW3DoXMsIHNlIGNvbnNpZGVyYSBsYSBQRUEsIHBhcmEgY2FsY3VsYXIgZWwgY29lZmljaWVudGUgUEJHUEVBIGVxdWl2YWxlbnRlIGFsIFBCSVBFQS4gRGUgZXN0YSBtYW5lcmEgc2UgZGV0ZXJtaW7DsyBjdcOhbGVzIHNvbiBsYXMgcmVncmVzaW9uZXMgcXVlIHNlIGRlYmVuIHV0aWxpemFyIHBhcmEgZGV0ZXJtaW5hciBlbCBMUEkuIEVuIGVsIGRhdGFzZXQgY29ycmVzcG9uZGllbnRlIHNlIG9ic2VydmFuIGxvcyB2YWxvcmVzIGNhcmFjdGVyw61zdGljb3MgZGUgbGEgcmVnacOzbiBkZSBNZW5kb3phLCBhbCBncnVwbyBxdWUgc2UgZGV0ZXJtaW7DsyBxdWUgcGVydGVuZWPDrWFuIHkgbGFzIHByZWRpY2Npb25lcyBkZSBsb3MgdmFsb3JlcyBkZSBMUEkgKGFkanVudG86IERhdG9zX016YV8xLnhscykgIAoKYGBge3J9CkRhdG9zX016YV8xIDwtIHJlYWRfZXhjZWwoIkRhdG9zX016YV8xLnhscyIpCmBgYAoKCiMjIERlc2Fycm9sbG8KCiMjIyBBbsOhbGlzaXMgZXhwbG9yYXRvcmlvCgpBIGNvbnRpbnVhY2nDs24gc2UgcHJvY2VkZXLDoSBhIGxhIGNvbmZvcm1hY2nDs24gZGUgbGEgYmFzZSBkZSBkYXRvcyBwYXJhIHJlYWxpemFyIGVsIGFuw6FsaXNpcyBleHBsb3JhdG9yaW8uIFBhcmEgcXVlIHNlYSBlcXVpZXZhbGVudGUgYWwgcGxhbnRlYWRvIGVuIFtAVGFwaWEyMDIzXSBzZSBwcm9jZWRlcsOhIGEgZWxpbWluYXIgbGFzIGZpbGFzIHF1ZSBwZXJ0ZW5lemNhbiBhIGxvcyBHcnVwb05DIHkgR3J1cG8wCgpgYGB7cn0KbGlicmFyeShkcGx5cikKCkRhdG9zX0N1cnNvQURfZXF1aXZhbGVudGUgPC0gRGF0b3NfQ3Vyc29BRF9jb21wbGV0b18xICU+JSAKICBmaWx0ZXIoIUdydXBvcyAlaW4lIGMoIkdydXBvTkMiLCAiR3J1cG8wIikpCmBgYAoKCmBgYHtyfQpEYXRvc19DdXJzb0FEXzEgPC0gRGF0b3NfQ3Vyc29BRF9lcXVpdmFsZW50ZSBbICxjKDMsNCw1LDYsNyw4LDksMTAsMTEsMTIsMTMsMTQsMTUpXQoKYGBgCgojIyMjIFJlbGFjaW9uZXMgZW50cmUgdmFyaWFibGVzCgpgYGB7cn0KbGlicmFyeSAoY2FyKQpzY2F0dGVycGxvdE1hdHJpeChEYXRvc19DdXJzb0FEXzEpCmBgYAoKIyMjIyBDb3JyZWxhY2lvbmVzIGVudHJlIHZhcmlhYmxlcwoKYGBge3J9CmxpYnJhcnkoY29ycnBsb3QpCk0gPC0gY29yIChEYXRvc19DdXJzb0FEXzEpCmNvcnJwbG90KE0pCmBgYAoKCmBgYHtyfQoKbGlicmFyeShwc3ljaCkKY29yUGxvdChEYXRvc19DdXJzb0FEXzEpCmBgYAoKU2Ugb2JzZXJ2YSBsYXMgbWF5b3JlcyBjb3JyZWxhY2lvbmVzIHBhcmEgZWwgTFBJIGNvbiBsYXMgdmFyaWFibGVzOiBQQklQRUEsIEFnciwgRXhwX01lcmMsIFJSTk4geSBFeHBfSF9UZWNoLgoKCiMjIyMgTWF0cml6IGRlIGNvdmFyaWFuemEgCgpgYGB7cn0KbGlicmFyeShzY2F0dGVyUGxvdE1hdHJpeCkKc2NhdHRlclBsb3RNYXRyaXgoRGF0b3NfQ3Vyc29BRF8xKQpgYGAKCgoKIyMjIyBEZXRlcm1pbmFjacOzbiBkZSBjb21wb25lbnRlcyBwcmluY2lwYWxlcwoKCgpgYGB7cn0KbWlucyA8LSBzYXBwbHkoRGF0b3NfQ3Vyc29BRF8xLCBtaW4pCm1heHMgPC0gc2FwcGx5KERhdG9zX0N1cnNvQURfMSwgbWF4KQoKYGBgCgpgYGB7cn0Kbm9ybWFsaXplIDwtIGZ1bmN0aW9uKHgsIG1pbl92YWwsIG1heF92YWwpewogICh4IC0gbWluX3ZhbCkgLyAobWF4X3ZhbCAtIG1pbl92YWwpCn0KCkRhdG9zX0N1cnNvQURfMV9ub3JtIDwtIGFzLmRhdGEuZnJhbWUoCiAgbWFwcGx5KGZ1bmN0aW9uKHgsIG1uLCBteCkgbm9ybWFsaXplKHgsIG1uLCBteCksCiAgICAgICAgIERhdG9zX0N1cnNvQURfMSwgbWlucywgbWF4cykKKQoKYGBgCgoKCgpgYGB7cn0KUEMxIDwtIHByaW5jb21wIChEYXRvc19DdXJzb0FEXzFfbm9ybSkKUEMxCmBgYAoKCgpgYGB7cn0KcGxvdChQQzEpCmBgYAoKYGBge3J9CmJpcGxvdChQQzEpCmBgYAoKYGBge3J9CmFjcDIgPC1QQzEkc2NvcmVzWyAsMToyXQpwbG90KGFjcDIpCmBgYAoKIyMjIyBFc3R1ZGlvIGRlIGNsw7pzdGVycwoKYS0gQWduZXMKCmBgYHtyfQpsaWJyYXJ5KGNsdXN0ZXIpCmFncDMgPSBhZ25lcyhhY3AyLG1ldGhvZD0iYXZlcmFnZSIpCnBsb3QoYWdwMykKYGBgCgpTZSBlc3R1ZGlhcsOhbiBsYSBjYW50aWRhZCBkZSA2IGNsw7pzdGVycyBxdWUgc29uIGxvcyBncnVwb3MgZGV0ZXJtaW5hZG9zIGVuIGVsIGVzdHVkaW8gZGUgYmFzZS4KCgpFc3R1ZGlvIHBvciBjb21wb25lbnRlcyBwcmluY2lwYWxlcwoKYGBge3J9CmFncGN1dCA8LSBjdXRyZWUoYWdwMyw2KQpwYXIobWZyb3c9YygxLDEpKQpwbG90KGFjcDIsY29sPWFncGN1dCkKYGBgCgpiLSBDbGFyYQoKYGBge3J9CmxpYnJhcnkoY2x1c3RlcikKcGxvdChjbGFyYShEYXRvc19DdXJzb0FEXzEsNikpCmBgYAoKU2UgYWdyZWdhcsOhbiBsb3MgbsO6bWVyb3MgZGUgY2zDunN0ZXJzIGFsIGRhdGFzZXQgaW5pY2lhbAoKYGBge3J9Cm1vZGVsbyA8LSBjbGFyYShEYXRvc19DdXJzb0FEXzEsIGsgPSA2KQptb2RlbG8kY2x1c3RlcmluZwpgYGAKCmBgYHtyfQpjbHVzdGVycyA8LSBzcGxpdCgxOm5yb3coRGF0b3NfQ3Vyc29BRF9lcXVpdmFsZW50ZSksIG1vZGVsbyRjbHVzdGVyaW5nKQpjbHVzdGVycwpgYGAKCmBgYHtyfQpEYXRvc19DdXJzb0FEX2VxdWl2YWxlbnRlJGNsdXN0ZXIgPC0gbW9kZWxvJGNsdXN0ZXJpbmcKCmBgYApTZSBvYnNlcnZhIHF1ZSBsYSBjbGFzaWZpY2FjacOzbiBkZSBjbMO6c3RlcnMgdXRpbGl6YWRhIHBvciBlbCBtw6l0b2RvIENsYXJhIG5vIGVzIGNvbXBhcmFibGUgY29uIGxhIGNsYXNpZmljYWNpw7NuIGVuIGxhIGZ1ZW50ZSBbQFRhcGlhMjAyM10KCmMtIFdhcmQKClNlIHV0aWxpemFyw6EgZWwgbcOpdG9kbyBXYXJkLCBxdWUgZXMgZWwgcXVlIHNlIHV0aWxpesOzIGVuIFtAVGFwaWEyMDIzXQoKYGBge3J9CiMgMS4gRXNjYWxhciBkYXRvcyBudW3DqXJpY29zCkRhdG9zX0N1cnNvQURfMXNjYWxlZCA8LSBzY2FsZShEYXRvc19DdXJzb0FEXzEpCgojIDIuIENhbGN1bGFyIGRpc3RhbmNpYSBldWNsaWRpYW5hCmRpc3RfZXVjbGlkIDwtIGRpc3QoRGF0b3NfQ3Vyc29BRF8xc2NhbGVkLCBtZXRob2QgPSAiZXVjbGlkZWFuIikKCiMgMy4gQ2x1c3RlcmluZyBqZXLDoXJxdWljbyBjb24gbcOpdG9kbyBXYXJkCmhjX3dhcmQgPC0gaGNsdXN0KGRpc3RfZXVjbGlkLCBtZXRob2QgPSAid2FyZC5EMiIpCgojIDQuIEdyYWZpY2FyIGRlbmRyb2dyYW1hCnBsb3QoaGNfd2FyZCwgCiAgICAgbWFpbiA9ICJDbHVzdGVyaW5nIEplcsOhcnF1aWNvIC0gTcOpdG9kbyBXYXJkIChEMikiLAogICAgIHhsYWIgPSAiIiwKICAgICB5bGFiID0gIkFsdHVyYSIsCiAgICAgc3ViID0gIiIpCgojIDUuIENvcnRhciBlbCDDoXJib2wgZW4gayBjbHVzdGVycyAoZWo6IDMgY2x1c3RlcnMpCm1vZGVsbzIgPC0gY3V0cmVlKGhjX3dhcmQsIGsgPSA2KQoKIyA2LiBBZ3JlZ2FyIGxhIGNsYXNpZmljYWNpw7NuIGFsIGRhdGFzZXQKRGF0b3NfQ3Vyc29BRF9jbHVzdGVyIDwtIGFzLmZhY3Rvcihtb2RlbG8yKQoKIyBWZXIgY2x1c3RlcnMKdGFibGUoRGF0b3NfQ3Vyc29BRF9jbHVzdGVyKQptb2RlbG8yCmBgYAoKClNlIGFncmVnYXLDoW4gbG9zIG7Dum1lcm9zIGRlIGNsw7pzdGVycyBhbCBkYXRhc2V0IGluaWNpYWwKCgoKYGBge3J9CmNsdXN0ZXJzMiA8LSBzcGxpdCgxOm5yb3coRGF0b3NfQ3Vyc29BRF9lcXVpdmFsZW50ZSksIG1vZGVsbzIpCmNsdXN0ZXJzMgoKYGBgCgpgYGB7cn0KRGF0b3NfQ3Vyc29BRF9lcXVpdmFsZW50ZSRjbHVzdGVyMiA8LSBtb2RlbG8yCgpgYGAKCgpgYGB7cn0KbGlicmFyeShvcGVueGxzeCkKCndyaXRlLnhsc3goRGF0b3NfQ3Vyc29BRF9lcXVpdmFsZW50ZSwgIkRhdG9zX0N1cnNvQURfZXF1aXZhbGVudGUueGxzeCIpCgpgYGAKCkRlIGxhcyBkb3MgbWFuZXJhcyBkZSBvYnRlbmVyIGNsw7pzdGVycyBubyBzZSBvYnNlcnZhIGRlbWFzaWFkYSBjb25pbmNpZGVuY2lhIGNvbiBsbyBwbGFudGVhZG8gZW4gW0BUYXBpYTIwMjNdCgoKIyMgTW9kZWxvcyBwcmVkaWN0aXZvcwoKCiMjIyDDgXJib2xlcyBkZSBkZWNpc2lvbmVzCgpgYGB7cn0KbGlicmFyeShycGFydCkKbGlicmFyeShycGFydC5wbG90KSAgICMgcGFyYSBncmFmaWNhciBlbCDDoXJib2wKbGlicmFyeSAoY2FyKQpsaWJyYXJ5IChwYXJ0eSkKCmBgYAoKYGBge3J9CkRhdG9zX0N1cnNvQURfVHJlZSA8LSBEYXRvc19DdXJzb0FEX2VxdWl2YWxlbnRlIFsgLGMoMyw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNSwxNildCgpEYXRvc19DdXJzb0FEX1RyZWUkR3J1cG9zIDwtIGFzLmZhY3RvcihEYXRvc19DdXJzb0FEX1RyZWUkR3J1cG9zKQoKCmBgYAoKQ2xhc2lmaWNhY2nDs24gdXRpbGl6YW5kbyBMUEkgeSBvdHJvIHRpcG8gZGUgYWxnb3JpdG1vIHBhcmEgZWwgw6FyYm9sLiAKCgpgYGB7cn0KYXJib2wgPC0gcnBhcnQoR3J1cG9zIH4gLiwgCiAgICAgICAgICAgICAgIGRhdGEgPSBEYXRvc19DdXJzb0FEX1RyZWUsIAogICAgICAgICAgICAgICBtZXRob2QgPSAiY2xhc3MiKSAgICMgY2xhc2lmaWNhY2nDs24KCmBgYAoKCmBgYHtyfQpycGFydC5wbG90KGFyYm9sLCB0eXBlID0gMiwgZXh0cmEgPSAxMDQsIGZhbGxlbi5sZWF2ZXMgPSBUUlVFKQoKYGBgCgoKYGBge3J9CnByZWQgPC0gcHJlZGljdChhcmJvbCwgRGF0b3NfQ3Vyc29BRF9UcmVlLCB0eXBlID0gImNsYXNzIikKCnRhYmxlKFByZWRpY2Npw7NuID0gcHJlZCwgUmVhbCA9IERhdG9zX0N1cnNvQURfVHJlZSRHcnVwb3MpCgoKYGBgCgpTZSBvYnNlcnZhIHVuYSBtYXRyaXogZGUgY29uZnVzacOzbiBjb24gdW5hIGRpYWdvbmFsIHByaW5jaXBhbCBkb21pbmFudGUuIENvbiBsYSBzaWd1aWVudGUgZWZlY3RpdmlkYWQgZGUgcHJlZGljY2nDs24uIAoKYGBge3J9Cm1lYW4ocHJlZCA9PSBEYXRvc19DdXJzb0FEX1RyZWUkR3J1cG9zKQoKYGBgCgoKYGBge3J9CnByZWQxIDwtIHByZWRpY3QoYXJib2wsIERhdG9zX016YV8xIFsgLCBjICgyLDMsNCw1LDYsNyw4LDksMTAsMTEsMTIsMTMsMTQpXSwgdHlwZSA9ICJjbGFzcyIpCnByZWQxCmBgYAoKCgpgYGB7cn0KTXphMiA8LSBzcGxpdCgxOm5yb3coRGF0b3NfTXphXzEpLCBwcmVkMSkKTXphMgpgYGAKCgpgYGB7cn0KRGF0b3NfTXphXzEkTXphMiA8LSBwcmVkMQoKd3JpdGUueGxzeChEYXRvc19NemFfMSwgIkRhdG9zX016YV8xLnhsc3giKQoKYGBgCgoKQ29tcGFyYW5kbyBsb3MgcmVzdWx0YWRvcyBkZWwgbW9kZWxvIHByZWRpY3Rpdm8gZGUgR3J1cG9zIHkgbGEgY2xhc2lmaWNhY2nDs24gaGVjaGEgZW4gW0BUYXBpYTIwMjNdIHNlIG9ic2VydmEgdW5hIGVxdWl2YWxlbmNpYSBkZWwgODksNDclLgoKCiMjIyBSZWRlcyBOZXVyb25hbGVzCgpTZSByZWFsaXphcsOhIHVuYSBSZWQgTmV1cm9uYWwgcGFyYSBwcmVkZWNpciBsb3MgdmFsb3JlcyBkZSBMUEkgeSBjb21wYXJhcmxvcyBjb24gbGFzIHByZWRpY2Npb25lcyBkZSBbQFRhcGlhMjAyM10gcGFyYSBNZW5kb3phLgoKUHJpbWVybyBzZSByZWFsaXphIHVuIHByb2Nlc28gZGUgbm9ybWFsaXphY2nDs24uIAoKYGBge3J9Cm1pbnMgPC0gc2FwcGx5KERhdG9zX0N1cnNvQURfMSwgbWluKQptYXhzIDwtIHNhcHBseShEYXRvc19DdXJzb0FEXzEsIG1heCkKCmBgYAoKYGBge3J9Cm5vcm1hbGl6ZSA8LSBmdW5jdGlvbih4LCBtaW5fdmFsLCBtYXhfdmFsKXsKICAoeCAtIG1pbl92YWwpIC8gKG1heF92YWwgLSBtaW5fdmFsKQp9CgpEYXRvc19DdXJzb0FEXzFfbm9ybSA8LSBhcy5kYXRhLmZyYW1lKAogIG1hcHBseShmdW5jdGlvbih4LCBtbiwgbXgpIG5vcm1hbGl6ZSh4LCBtbiwgbXgpLAogICAgICAgICBEYXRvc19DdXJzb0FEXzEsIG1pbnMsIG1heHMpCikKCmBgYAoKClNlZ3VuZG8gc2UgZGl2aWRlIGVsIGRhdGFzZXQgZW4gdW5hIG11ZXN0cmEgZGUgZW50cmVuYW1pZW50byB5IHVuYSBtdWVzdHJhIHBhcmEgdGVzdGVvLiAKCmBgYHtyfQppbmRpY2UgPC0gc2FtcGxlKDIsIG5yb3coRGF0b3NfQ3Vyc29BRF8xX25vcm0pLCByZXBsYWNlID0gVFJVRSwgcHJvYiA9IGMoMC43LDAuMykpCkxQSV90cmFpbiA8LSBEYXRvc19DdXJzb0FEXzFfbm9ybSBbaW5kaWNlPT0xLF0KTFBJX3Rlc3QgIDwtIERhdG9zX0N1cnNvQURfMV9ub3JtIFtpbmRpY2U9PTIsXQoKYGBgCgpNb2RlbG8gcHJlZGljdGl2byBkZSByZWQgbmV1cm9uYWwuIAoKYGBge3J9CmxpYnJhcnkobmV1cmFsbmV0KQoKTFBJX21vZGVsIDwtIG5ldXJhbG5ldChMUEkgfiBQQklQRUEgKyBBZ3IgKyBJbmQgKyBDb21lcmNpbyArIEV4cF9NZXIgKyBFeHBfQnlTICsgRXhwX0NvbWIgKyBFeHBfTU0gKyBQcm9kX29pbCArIFJSTk4gKyBUaWVycmFzX0FnciArIEV4cF9IX1RlY2ggLCBkYXRhID0gTFBJX3RyYWluLCBoaWRkZW4gPSBjKDIsMSkpCgpzdHIoTFBJX21vZGVsKQpgYGAKCgpgYGB7cn0KbGlicmFyeShOZXVyYWxOZXRUb29scykKCnBsb3RuZXQoTFBJX21vZGVsLCAKICAgICAgICBhbHBoYSA9IDAuNywKICAgICAgICBub2RlX2xhYnMgPSBUUlVFLAogICAgICAgIHlfbmFtZXMgPSAiTFBJX3ByZWQiLAogICAgICAgIGNpcmNsZV9jZXggPSA1LAogICAgICAgIGNleF92YWwgPSAwLjYpCgpgYGAKClNlIGNvbXBydWViYSBlbCBuaXZlbCBkZSBlZmljaWVuY2lhIGRlbCBtb2RlbG8gY29uIHJlc3BldG8gYWwgc2V0IGRlIHRlc3RlbwoKYGBge3J9Cm1vZGVsX3Jlc3VsdHMgPC0gY29tcHV0ZShMUElfbW9kZWwsTFBJX3Rlc3RbMToxM10pCnByZWRpY3RlZF9MUEkgPC0gbW9kZWxfcmVzdWx0cyRuZXQucmVzdWx0CmBgYAoKYGBge3J9CmNvcihwcmVkaWN0ZWRfTFBJLExQSV90ZXN0JExQSSkKYGBgCgpFbCBuaXZlbCBkZSBjb3JyZWxhY2nDs24gZGVsIHRlc3RlbyBlcyBkZWwgODYlCgpBIGNvbnRpbnVhY2nDs24sIHNlIHJlYWxpemEgbGEgcHJlZGljY2nDs24gZGUgbG9zIHZhbG9yZXMgcGFyYSBNZW5kb3phLgoKCkJhc2UgZGUgZGF0b3MgeSBub3JtYWxpemFjacOzbiBlcXVpdmFsZW50ZSBhbCBzZXQgZGUgZGF0b3Mgb3JpZ2luYWxlcwoKYGBge3J9Ck16YV90ZXN0PC0gRGF0b3NfTXphXzEgWyAsYygyLDMsNCw1LDYsNyw4LDksMTAsMTEsMTIsMTMsMTQpXQpNemFfdGVzdF9ub3JtIDwtIGFzLmRhdGEuZnJhbWUoCiAgbWFwcGx5KGZ1bmN0aW9uKHgsIG1uLCBteCkgbm9ybWFsaXplKHgsIG1uLCBteCksCiAgICAgICAgIE16YV90ZXN0LCBtaW5zLCBtYXhzKQopCgpgYGAKClByZWRpY2Npb25lcyBkZSBNZW5kb3phCgpgYGB7cn0KTXphX3Jlc3VsdHMgPC0gY29tcHV0ZShMUElfbW9kZWwsTXphX3Rlc3Rfbm9ybSBbMToxMl0pCk16YV9MUElfbm9tciA8LSBNemFfcmVzdWx0cyRuZXQucmVzdWx0CgpgYGAKCgpgYGB7cn0KTXphX0xQSSA8LSBNemFfTFBJX25vbXIgKiAobWF4c1siTFBJIl0gLSBtaW5zWyJMUEkiXSkgKyBtaW5zWyJMUEkiXQpNemFfTFBJCgpgYGAKCkNvbXBhcmFjacOzbiBkZSBjb3JyZWxhY2nDs24gZW50cmUgbG9zIHZhbG9yZXMgZGV0ZXJtaW5hZG9zIHBvciBsYSByZWQgbmV1cm9uYWwgeSBlbCBtw6l0b2RvIGRlIHJlZ3Jlc2lvbmVzIGxpbmVhbGVzIG9ic2VydmFkb3MgZW4gW0BUYXBpYTIwMjNdCgpgYGB7cn0KY29yKE16YV9MUEksRGF0b3NfTXphXzEkTFBJKQpgYGAKCgpgYGB7cn0KTXphMyA8LSBzcGxpdCgxOm5yb3coRGF0b3NfTXphXzEpLCBNemFfTFBJKQoKYGBgCgoKYGBge3J9CkRhdG9zX016YV8xJE16YTMgPC0gTXphX0xQSQoKd3JpdGUueGxzeChEYXRvc19NemFfMSwgIkRhdG9zX016YV8xLnhsc3giKQpgYGAKCgojIyBEaXNjdWNpw7NuIGRlIFJlc3VsdGFkb3MKCiMjIyBDbGFzaWZpY2FjacOzbiBwb3IgZ3J1cG9zCgojIyMjIGEuIENsYXNpZmljYWNpw7NuIHBvciBtZWRpbyBkZSBDbMO6c3RlcnMgCiAgICAgICAgCk5vIHNlIG9ic2VydmFuIGRlbWFzaWFkYXMgc2ltaWxpdHVkZXMgZW50cmUgbG9zIGdydXBvcyBkZXRlcm1pbmFkb3MgZW4gW0BUYXBpYTIwMjNdIHkgbG9zIG9idGVuaWRvcyBjb24gbG9zIG3DqXRvZG9zIGRlIGNsw7pzdGVycyB1dGlsaXphZG9zIGVuIGVzdGEgaW52ZXN0aWdhY2nDs24uIAoKIyMjIyBiLiBDbGFzaWZpY2FjacOzbiBkZSBncnVwb3MgYXBsaWNhZG9zIGEgTWVuZG96YQoKQ3VhbmRvIHNlIHJlYWxpemEgZXN0ZSB0aXBvIGRlIGNsYXNpZmljYWNpw7NuIHNlZ8O6biBsYSBiYXNlIGRlIGRhdG9zIG9yaWdpbmFsLCB1dGlsaXphbmRvIGxvcyBHcnVwb3MgcHJlY2xhc2lmaWNhZG9zLiBTZSB1dGlsaXphIHVuIG3DqXRvZG8gcHJlZGljdGl2byBwb3IgbWVkaW8gZGUgw6FyYm9sZXMuIEFsIGNvbXBhcmFyIGxvcyByZXN1bHRhZG9zIGRlbCBtw6l0b2RvIHByZWRpY3Rpdm8geSBsbyBkZXRlcm1pbmFkbyBwYXJhIE1lbmRvemEgZW4gZWwgdHJhYmFqbyBvcmlnaW5hbCwgc2Ugb2J0aWVuZSB1bmEgZWZlY3RpdmlkYWQgZGVsIDg5JSBkZSBsb3MgY2Fzb3MuIAogICAgICAgIApFc3RhIGVzIGV2aWRlbmNpYSBkZSBxdWUgZWwgbcOpdG9kbyBkZSBjbGFzaWZpY2FjacOzbiBwb3IgZ3J1cG9zIHV0aWxpemFkbyBlbiAgW0BUYXBpYTIwMjNdIHRpZW5lIHVuYSBlZmVjdGl2aWRhZCBkZSBjYXNpIGVsIDkwJS4gCiAgICAgIAojIyMgTW9kZWxvcyBwcmVkaWN0aXZvIGRlIExQSQoKRWwgZGVzYXJyb2xsbyBkZWwgbW9kZWxvIHByZWRpY3Rpdm8gYmFzYWRvIGVuIFJlZGVzIE5ldXJvbmFsZXMgcHJlc2VudGEgdW4gYnVlbiBjb2VmaWVudGUgZGUgY29ycmVsYWNpw7NuIGRlbCA4MiUuIENvbXBhcmFibGUgY29uIGVsIHJhbmdvIGRlIHZhbG9yZXMgZGUgZWZpY2llbmNpYSBkZSBsYXMgcmVncmVzaW9uZXMgbGluZWFsZXMgcHJlc2VudGFkb3MgZW4gW0BUYXBpYTIwMjNdLCB2ZXIgZmlndXJhIHNpZ3VpZW50ZS4gCiAgICAgICAgCgpgYGB7ciBmaWcuY2FwPSJGaWd1cmEgMSDigJMgRWZpY2llbmNpYSBkZSBSZWdyZXNpb25lcyBsaW5lYWxlcyBwb3IgZ3J1cG9zLCBleHRyYcOtZG8gW0BUYXBpYTIwMjFdIiwgb3V0LndpZHRoPSI1MCUiLCBmaWcuYWxpZ249ImNlbnRlciJ9CmxpYnJhcnkgKGtuaXRyKQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyAoIlRlc3QtcmVncmVzaW9uZXMucG5nIikKCmBgYAoKU2kgc2UgY29tcGFyYSBsYSBwcmVkaWNjacOzbiBwYXJhIE1lbmRvemEgY29uICByZWRlcyBuZXVyb25hbGVzIHkgY29uIGVsIG1vZGVsbyBkZSByZWdyZXNpb25lcyBsaW5lYWxlcywgc2Ugb2JzZXJ2YSB1bmEgY29ycmVsYWNpw7NuIGFjZXB0YWJsZSBkZSAwLDcyLiBMYSB2ZW50YWphIGRlbCBtb2RlbG8gY29uIHJlZGVzIG5ldXJvbmFsZXMgdGllbmUgbGEgdmVudGFqYSBkZSBxdWUgbm8gbmVjZXNpdGEgdW5hIGNsYXNpZmljYWNpw7NuIHBvciBncnVwb3MuIAoKCiMjIENvbmNsdXNpb25lcwoKRWwgZXN0dWRpbyBwcmVzZW50YWRvIGNvbnN0aXR1eWUgdW4gYXBvcnRlIHNpZ25pZmljYXRpdm8gYWwgYW7DoWxpc2lzIHkgcHJlZGljY2nDs24gZGVsIHJlbmRpbWllbnRvIGxvZ8Otc3RpY28gcmVnaW9uYWwsIGVzcGVjaWFsbWVudGUgZW4gY29udGV4dG9zIGRvbmRlIGxhIGluZm9ybWFjacOzbiBkaXJlY3RhIGVzIGVzY2FzYSBvIGluZXhpc3RlbnRlLiBFbiBwcmltZXIgbHVnYXIsIHF1ZWRhIGRlbW9zdHJhZGEgbGEgcmVsZXZhbmNpYSBkZWwgTFBJIGNvbW8gaW5kaWNhZG9yIHNpbnTDqXRpY28geSByb2J1c3RvIGEgbml2ZWwgaW50ZXJuYWNpb25hbCwgYXPDrSBjb21vIGxhIHBlcnRpbmVuY2lhIGRlIHV0aWxpemFyIHZhcmlhYmxlcyBzb2Npb2Vjb27Ds21pY2FzIHBhcmEgY29uc3RydWlyIGFwcm94aW1hY2lvbmVzIHByZWRpY3RpdmFzIGRlIGJham8gY29zdG8uIExhIGJhc2UgZGUgZGF0b3MgdXRpbGl6YWRhIHkgYW1wbGlhZGEgZW4gZXN0YSBpbnZlc3RpZ2FjacOzbiBwZXJtaXRlIGVzdGFibGVjZXIgcmVsYWNpb25lcyB2YWxpb3NhcyBlbnRyZSBjYXJhY3RlcsOtc3RpY2FzIGVjb27Ds21pY2FzIGVzdHJ1Y3R1cmFsZXMgeSBkZXNlbXBlw7FvIGxvZ8Otc3RpY28sIGdlbmVyYW5kbyB1biBtYXJjbyBhZGVjdWFkbyBwYXJhIGxhIGNvbXBhcmFjacOzbiBkZSBkaXZlcnNvcyBtw6l0b2RvcyBkZSBhbmFsw610aWNhIGRlIGRhdG9zLgoKRW4gcmVsYWNpw7NuIGNvbiBsb3MgbcOpdG9kb3MgcHJlZGljdGl2b3MsIGVsIGFuw6FsaXNpcyBldmlkZW5jaWEgZGlmZXJlbmNpYXMgc3VzdGFudGl2YXMgZW50cmUgbGFzIHTDqWNuaWNhcyBhcGxpY2FkYXMuIExvcyBtw6l0b2RvcyBkZSBjbHVzdGVyaW5nIOKAlHRhbnRvIHBvciBDbGFyYSBjb21vIHBvciBBZ25lcyB5IFdhcmTigJQgbW9zdHJhcm9uIGRpZmljdWx0YWRlcyBwYXJhIHJlcHJvZHVjaXIgbG9zIGdydXBvcyBwcmV2aWFtZW50ZSBlc3RhYmxlY2lkb3MgZW4gW0BUYXBpYTIwMjNdLiBFc3RvIHN1Z2llcmUgcXVlIGxhIGNsYXNpZmljYWNpw7NuIG9yaWdpbmFsIGRlcGVuZGUgZGUgY3JpdGVyaW9zIGVzcGVjw61maWNvcyByZWxhY2lvbmFkb3MgY29uIGxhcyBhY3RpdmlkYWRlcyBlY29uw7NtaWNhcyB5IGxhIGVzdHJ1Y3R1cmEgcHJvZHVjdGl2YSBkZSBsb3MgcGHDrXNlcywgY3JpdGVyaW9zIHF1ZSBubyBlbWVyZ2VuIGRlIG1hbmVyYSBlc3BvbnTDoW5lYSBtZWRpYW50ZSBhbGdvcml0bW9zIGRlIGFncnVwYW1pZW50byBubyBzdXBlcnZpc2Fkby4gU2luIGVtYmFyZ28sIGFsIGFwbGljYXIgdW4gcHJvY2VzbyBzdXBlcnZpc2FkbyBwYXJhIGxhIGNsYXNpZmljYWNpw7NuIGRlIGdydXBvcywgZWwgw6FyYm9sIGRlIGRlY2lzacOzbiBvYnR1dm8gdW5hIHByZWNpc2nDs24gZGVsIDg5JSwgbG8gcXVlIGluZGljYSBxdWUgbG9zIHBhdHJvbmVzIHByZXNlbnRlcyBlbiBsYSBjbGFzaWZpY2FjacOzbiBvcmlnaW5hbCBwdWVkZW4gc2VyIGNhcHR1cmFkb3MgYWRlY3VhZGFtZW50ZSBjdWFuZG8gbGEgdMOpY25pY2EgaW5jb3Jwb3JhIGluZm9ybWFjacOzbiBwcmV2aWEuCgpFbCBtb2RlbG8gZGUgcmVkZXMgbmV1cm9uYWxlcyBtb3N0csOzIHRhbWJpw6luIHJlc3VsdGFkb3MgcHJvbWV0ZWRvcmVzLiBDb24gdW5hIGNvcnJlbGFjacOzbiBkZWwgODIlIGFsIGV2YWx1YXIgZWwgY29uanVudG8gZGUgdGVzdGVvLCBzZSBldmlkZW5jaWEgcXVlIGVzdGUgdGlwbyBkZSBtb2RlbG9zIHRpZW5lIGNhcGFjaWRhZCBwYXJhIGFwcmVuZGVyIHJlbGFjaW9uZXMgbm8gbGluZWFsZXMgZW50cmUgbGFzIHZhcmlhYmxlcyBzb2Npb2Vjb27Ds21pY2FzIHkgZWwgTFBJLCBzdXBlcmFuZG8gZW4gZmxleGliaWxpZGFkIGEgbG9zIG1vZGVsb3MgbGluZWFsZXMgdHJhZGljaW9uYWxlcy4gTcOhcyBhw7puLCBhbCBjb21wYXJhciBsYXMgcHJlZGljY2lvbmVzIHBhcmEgTWVuZG96YSBjb24gbGFzIGVzdGltYWNpb25lcyBvYnRlbmlkYXMgbWVkaWFudGUgZWwgbcOpdG9kbyBkZSByZWdyZXNpb25lcyBsaW5lYWxlcywgc2Ugb2JzZXJ2w7MgdW5hIGNvcnJlbGFjacOzbiBhY2VwdGFibGUgZGUgMCw3Mi4gRXN0ZSByZXN1bHRhZG8gY29uZmlybWEgcXVlIGxhcyByZWRlcyBuZXVyb25hbGVzIHB1ZWRlbiBzZXIgdW5hIGhlcnJhbWllbnRhIGNvbXBsZW1lbnRhcmlhIOKAlGUgaW5jbHVzbyBhbHRlcm5hdGl2YeKAlCBwYXJhIGxhIHByZWRpY2Npw7NuIGRlbCByZW5kaW1pZW50byBsb2fDrXN0aWNvIHNpbiBsYSBuZWNlc2lkYWQgZGUgY2xhc2lmaWNhciBwcmV2aWFtZW50ZSBhIGxhcyByZWdpb25lcyBlbiBncnVwb3MuCgpEZXNkZSB1bmEgcGVyc3BlY3RpdmEgYXBsaWNhZGEsIGVzdGUgdHJhYmFqbyBkZW11ZXN0cmEgbGEgdXRpbGlkYWQgZGUgbG9zIG3DqXRvZG9zIGRlIGFuYWzDrXRpY2EgZGUgZGF0b3MgZW4gZWwgZGlzZcOxbyBkZSBoZXJyYW1pZW50YXMgcHJlZGljdGl2YXMgcGFyYSBsYSBwbGFuaWZpY2FjacOzbiBsb2fDrXN0aWNhIHJlZ2lvbmFsLiBMYSBwb3NpYmlsaWRhZCBkZSBlc3RpbWFyIGVsIHJlbmRpbWllbnRvIGxvZ8Otc3RpY28gY29uIGluZm9ybWFjacOzbiBkaXNwb25pYmxlIHDDumJsaWNhbWVudGUgZmFjaWxpdGEgbGEgdG9tYSBkZSBkZWNpc2lvbmVzIGVuIHBvbMOtdGljYXMgcMO6YmxpY2FzLCBlc3BlY2lhbG1lbnRlIGVuIHRlcnJpdG9yaW9zIGRvbmRlIG5vIGV4aXN0ZW4gbWVkaWNpb25lcyBlc3RhbmRhcml6YWRhcy4gQWRlbcOhcywgbGEgY29tcGFyYWNpw7NuIGRlIG3DqXRvZG9zIHBlcm1pdGUgaWRlbnRpZmljYXIgZm9ydGFsZXphcyB5IGxpbWl0YWNpb25lcyBkZSBjYWRhIGVuZm9xdWU6IGxhcyByZWdyZXNpb25lcyBsaW5lYWxlcyBvZnJlY2VuIGludGVycHJldGFiaWxpZGFkIHkgc2ltcGxpY2lkYWQ7IGxvcyDDoXJib2xlcyBkZSBkZWNpc2nDs24gYXBvcnRhbiBjbGFyaWRhZCBlbiBsYSBjbGFzaWZpY2FjacOzbjsgbGFzIHJlZGVzIG5ldXJvbmFsZXMgcGVybWl0ZW4gY2FwdHVyYXIgcmVsYWNpb25lcyBjb21wbGVqYXMgZW50cmUgbcO6bHRpcGxlcyBmYWN0b3Jlcy4KCkZpbmFsbWVudGUsIGVsIGFuw6FsaXNpcyByZWFsaXphZG8gcGFyYSBsYXMgcmVnaW9uZXMgZGUgTWVuZG96YSBtdWVzdHJhIGVsIHBvdGVuY2lhbCBkZWwgZW5mb3F1ZSBwcmVkaWN0aXZvIHBhcmEgYXBsaWNhY2lvbmVzIHRlcnJpdG9yaWFsZXMgY29uY3JldGFzLiBMYSBpZGVudGlmaWNhY2nDs24gZGUgdmFyaWFjaW9uZXMgZW4gZWwgcmVuZGltaWVudG8gbG9nw61zdGljbyBlbnRyZSBkZXBhcnRhbWVudG9zIGFwb3J0YSBpbmZvcm1hY2nDs24gZXN0cmF0w6lnaWNhIHBhcmEgb3JpZW50YXIgaW52ZXJzaW9uZXMgZW4gaW5mcmFlc3RydWN0dXJhLCBvcHRpbWl6YXIgbGEgcGxhbmlmaWNhY2nDs24geSBldmFsdWFyIGRlc2lndWFsZGFkZXMgdGVycml0b3JpYWxlcy4gRW4gY29uanVudG8sIGVsIGVzdHVkaW8gY29uZmlybWEgcXVlIGxhIGNvbWJpbmFjacOzbiBkZSBkYXRvcyBzb2Npb2Vjb27Ds21pY29zIGUgaW50ZWxpZ2VuY2lhIGFydGlmaWNpYWwgY29uc3RpdHV5ZSB1bmEgaGVycmFtaWVudGEgc8OzbGlkYSB5IGVmaWNheiBwYXJhIGVzdGltYXIgaW5kaWNhZG9yZXMgbG9nw61zdGljb3MgeSBtZWpvcmFyIGxhIGNvbXByZW5zacOzbiBkZWwgc2lzdGVtYSBwcm9kdWN0aXZvIHkgdGVycml0b3JpYWwuCgoKIyMgUmVmZXJlbmNpYXMKCmBgYHtyfQojIFJlZmVyZW5jaWFzCmBgYAoKCg==